bedrock-protocol/src/server.js
extremeheat 49cff40097 cliet auth
2021-02-10 05:04:10 -05:00

147 lines
No EOL
4.2 KiB
JavaScript

const Listener = require('@jsprismarine/raknet/listener')
const { EventEmitter } = require('events')
const { createDeserializer, createSerializer } = require('./transforms/serializer')
const { Encrypt } = require('./auth/encryption')
const { decodeLoginJWT } = require('./auth/chains')
const { Connection } = require('./connection')
const Options = require('./options')
const log = (...args) => console.log(...args)
class Player extends Connection {
constructor(server, connection, options) {
super()
this.server = server
this.serializer = server.serializer
this.connection = connection
Encrypt(this, server, options)
}
getData() {
return this.userData
}
onLogin(packet) {
let body = packet.data
console.log('Body', body)
const clientVer = body.protocol_version
if (this.server.options.version) {
if (this.server.options.version < clientVer) {
this.sendDisconnectStatus(failed_client)
return
}
} else if (clientVer < MIN_VERSION) {
this.sendDisconnectStatus(failed_client)
return
}
// Parse login data
const authChain = JSON.parse(body.params.chain)
const skinChain = body.params.client_data
try {
var { key, userData, chain } = decodeLoginJWT(authChain.chain, skinChain)
} catch (e) {
console.error(e)
throw new Error('Failed to verify user')
}
console.log('Verified user', 'got pub key', key, userData)
this.emit('login', { user: userData.extraData }) // emit events for user
this.emit('server.client_handshake', { key }) // internal so we start encryption
this.userData = userData.extraData
this.version = clientVer
}
sendDisconnectStatus(play_status) {
this.write('play_status', { status: play_status })
this.connection.close()
}
// After sending Server to Client Handshake, this handles the client's
// Client to Server handshake response. This indicates successful encryption
onHandshake() {
// https://wiki.vg/Bedrock_Protocol#Play_Status
this.write('play_status', { status: 'login_success' })
this.emit('join')
}
readPacket(packet) {
console.log('packet', packet)
const des = this.server.deserializer.parsePacketBuffer(packet)
console.log('->', des)
switch (des.data.name) {
case 'login':
console.log(des)
this.onLogin(des)
return
case 'client_to_server_handshake':
this.onHandshake()
default:
console.log('ignoring, unhandled')
}
this.emit(des.data.name, des.data.params)
}
}
class Server extends EventEmitter {
constructor(options) {
super()
this.options = { ...Options.defaultOptions, options }
this.serializer = createSerializer()
this.deserializer = createDeserializer()
this.clients = {}
this.validateOptions()
}
validateOptions() {
if (this.options.version < Options.MIN_VERSION) {
throw new Error(`Unsupported protocol version < ${Options.MIN_VERSION} : ${this.options.version}`)
}
}
getAddrHash(inetAddr) {
return inetAddr.address + '/' + inetAddr.port
}
onOpenConnection = (conn) => {
log('new connection', conn)
const player = new Player(this, conn)
this.clients[this.getAddrHash(conn.address)] = player
this.emit('connect', { client: player })
}
onCloseConnection = (inetAddr, reason) => {
log('close connection', inetAddr, reason)
delete this.clients[this.getAddrHash(inetAddr)]
}
onEncapsulated = (encapsulated, inetAddr) => {
log(inetAddr.address, ': Encapsulated', encapsulated)
const buffer = encapsulated.buffer
const client = this.clients[this.getAddrHash(inetAddr)]
if (!client) {
throw new Error(`packet from unknown inet addr: ${inetAddr.address}/${inetAddr.port}`)
}
client.handle(buffer)
}
async create(serverIp, port) {
this.listener = new Listener(this)
this.raknet = await this.listener.listen(serverIp, port)
log('Listening on', serverIp, port)
this.raknet.on('openConnection', this.onOpenConnection)
this.raknet.on('closeConnection', this.onCloseConnection)
this.raknet.on('encapsulated', this.onEncapsulated)
this.raknet.on('raw', (buffer, inetAddr) => {
console.log('Raw packet', buffer, inetAddr)
})
}
}
module.exports = { Server, Player }