From b50f6dc5e61b56a58f0ddd8af4bedba660bf7fcf Mon Sep 17 00:00:00 2001 From: Tau Date: Mon, 26 Nov 2018 00:37:48 -0500 Subject: [PATCH] Refactor the not-yet-working AimeDB impl --- src/aimedb.js | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/index.js | 63 ++---------------------- 2 files changed, 134 insertions(+), 59 deletions(-) create mode 100644 src/aimedb.js diff --git a/src/aimedb.js b/src/aimedb.js new file mode 100644 index 0000000..4cca1fc --- /dev/null +++ b/src/aimedb.js @@ -0,0 +1,130 @@ +const crypto = require('crypto') +const { Transform, pipeline } = require('stream') + +const K = Buffer.from('Copyright(C)SEGA', 'utf8') + +class Decoder extends Transform { + constructor(options) { + super({ + readableObjectMode: true, + ...options, + }) + + this.state = Buffer.alloc(0) + } + + _transform(chunk, encoding, callback) { + this.state = Buffer.concat([ this.state, chunk ]) + + if (this.state.length < 8) { + return + } + + const magic = this.state.readUInt16LE(0) + + if (magic != 0xA13E) { + return callback(new Error(`Invalid magic (decimal ${magic})`)) + } + + const len = this.state.readUInt16LE(6) + + if (this.state.length < len) { + return + } + + console.log('Aimedb: Recv', this.state) + + const cmd = this.state.readUInt16LE(4) + const payload = this.state.slice(8, len) + + this.state = this.state.slice(len) + + return callback(null, { cmd, payload }) + } +} + +class Encoder extends Transform { + constructor(options) { + super({ + writableObjectMode: true, + ...options + }) + } + + _transform(chunk, encoding, callback) { + const { cmd, payload } = chunk + + // Message size must be rounded up to a multiple of 16 + + const len = 8 + payload.length + const buf = Buffer.alloc((len + 15) & ~15) + + buf.writeUInt16LE(0xA13E, 0) // Magic? + buf.writeUInt16LE(0x3087, 2) // ??? + buf.writeUInt16LE(cmd, 4) + buf.writeUInt16LE(buf.length, 6) + payload.copy(buf, 8) + + console.log('Aimedb: Send', buf) + + callback(null, buf) + } +} + +async function dispatch(socket) { + console.log('Aimedb: Connection opened') + + const input = pipeline( + socket, + crypto + .createDecipheriv('aes-128-ecb', K, null) + .setAutoPadding(false), + new Decoder(), + ) + + + const output = new Encoder() + + pipeline( + output, + crypto + .createCipheriv('aes-128-ecb', K, null) + .setAutoPadding(false), + socket, + ) + + try { + for await (const req of input) { + let payload + + switch (req.cmd) { + case 0x0064: + console.log('Aimedb: Hello') + + payload = Buffer.alloc(24) + payload.writeInt16LE(0x0001, 0) + + output.write({ cmd: 0x0065, payload }) + + break + + case 0x0066: + console.log('Aimedb: Goodbye') + + break + + default: + console.log('Aimedb: Unknown command', req.cmd) + + break + } + } + } catch (e) { + console.log('Aimedb: Connection error:\n', e) + } + + console.log('Aimedb: Connection closed') + socket.end() +} + +module.exports = dispatch diff --git a/src/index.js b/src/index.js index ef2a7bd..c8d7e04 100644 --- a/src/index.js +++ b/src/index.js @@ -1,10 +1,11 @@ +const fs = require('fs') const https = require('https') const http = require('http') -const fs = require('fs') const net = require('net') + +const aimedb = require('./aimedb') const billing = require('./billing') const startup = require('./startup') -const crypto = require('crypto') const title = require('./title') const tls = { @@ -12,63 +13,7 @@ const tls = { key: fs.readFileSync('pki/server.key'), } -function encodeAimeBody(buf) { - const keyBuf = Buffer.from('Copyright(C)SEGA', 'utf8') - const ivBuf = Buffer.from('0x00', 'hex') - let cipher = crypto.createCipheriv('aes-128-ecb', keyBuf, ivBuf) - cipher.setAutoPadding(false) - crypted = Buffer.concat([cipher.update(buf), cipher.final()]) - return crypted -} -function decodeAimeBody(buf) { - const keyBuf = Buffer.from('Copyright(C)SEGA', 'utf8') - const ivBuf = Buffer.from('0x00', 'hex') - let decipher = crypto.createDecipheriv('aes-128-ecb', keyBuf, ivBuf) - decipher.setAutoPadding(false) - decrypted = Buffer.concat([decipher.update(buf), decipher.final()]) - return decrypted -} -function parseMessage(buf) { - switch(buf[4].toString(16)) { - case "64": - var newBuffer = new Buffer.from([0x3e, 0xa1, 0x87, 0x30, 0x65, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]); - console.log(newBuffer) - return newBuffer - break; - case "66": - var newBuffer = new Buffer.from([0x3e, 0xa1, 0x87, 0x30, 0x67, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]); - console.log(newBuffer) - return newBuffer - break; - default: - console.log("No case for "+buf[4].toString(16)) - return null - } - -} - -const server = net.createServer((c) => { - // 'connection' listener - console.log('client connected'); - c.on('end', () => { - console.log('client disconnected'); - }); - c.on('error', (err) => { throw err; }); - c.on('data', (data) => { - decode = decodeAimeBody(data); - encode = encodeAimeBody(decodeAimeBody(data)).toString('hex')+'\r\n' - console.log('Decode: '+decode.toString('hex')) - c.write(encodeAimeBody(parseMessage(decode))) - }); - c.pipe(c); -}); -server.on('error', (err) => { - throw err; -}); -server.listen(22345, () => { - console.log('server bound'); -}); - +net.createServer(aimedb).listen(22345) http.createServer(startup).listen(80) https.createServer(tls, billing).listen(8443) http.createServer(title).listen(9000)