diff --git a/datatypes.ts b/datatypes.ts index 4e87ea7..36748e0 100644 --- a/datatypes.ts +++ b/datatypes.ts @@ -33,8 +33,24 @@ export const CommandsByValue: t = Object.keys(Commands).reduce((acc: t, cur) => export const DrwStart = 0x0a11; export const ControlCommands = { + // TODO: flip these.. ConnectUser: 0x2010, ConnectUserAck: 0x2011, DevStatus: 0x0810, DevStatusAck: 0x0811, + ListWifi: 0x0360, + ListWifiAck: 0x0361, + WifiSettings: 0x0261, + StartVideo: 0x1030, +}; + +export const ccDest: Record = { + [ControlCommands.ConnectUser]: 0xff00, + [ControlCommands.DevStatus]: 0x0000, + [ControlCommands.ListWifi]: 0x0000, + [ControlCommands.ListWifiAck]: 0x0361, + [ControlCommands.StartVideo]: 0x0000, + [ControlCommands.ConnectUserAck]: 0xaa55, + [ControlCommands.DevStatusAck]: 0xaa55, + [ControlCommands.WifiSettings]: 0xaa55, }; diff --git a/handlers.ts b/handlers.ts index eea20f3..5f4fdbb 100644 --- a/handlers.ts +++ b/handlers.ts @@ -164,6 +164,6 @@ export const handle_Drw = (session: Session, dv: DataView) => { }; export const handle_P2PRdy = (session: Session, _: DataView) => { - const b = SendUsrChk("admin", "admin", session.outgoingCommandId); + const b = SendUsrChk(session, "admin", "admin"); session.send(b); }; diff --git a/impl.ts b/impl.ts index 09be60e..ba2e73e 100644 --- a/impl.ts +++ b/impl.ts @@ -1,6 +1,6 @@ import "./shim.ts"; -import { Commands } from "./datatypes.js"; +import { ControlCommands, Commands, ccDest } from "./datatypes.js"; import { Session } from "./server.js"; import { XqBytesEnc } from "./func_replacements.js"; import { hexdump } from "./hexdump.js"; @@ -29,97 +29,59 @@ const DrwHdr = (cmd: number, len: number, d1_or_d2: 0xd1 | 0xd2, m_chan: number, retret.add(6).writeU16(pkt_id); return retret; }; -export const SendDevStatus = (session: Session): DataView => { - let buf = new DataView(new Uint8Array(0x14).buffer); - /* -00000000 f1 d0 00 10 d1 00 00 c3 11 0a 08 10 04 00 00 00 ................ -00000010 68 66 6b 67 hfkg -*/ - let bytes = [ - 0xf1, - 0xd0, - 0x00, // len? lower values= no response, larger values = 1 frame then kicked - 0x10, // len - 0xd1, // ? - 0x00, // chan - session.outgoingCommandId >> 8, - session.outgoingCommandId, - 0x11, - 0x0a, - 0x08, - 0x10, - 0x04, // len - 0x00, // len - 0x00, // dst - 0x00, // dst - session.ticket[0], - session.ticket[1], - session.ticket[2], - session.ticket[3], - ]; - buf.writeByteArray(bytes); - return buf; +const makeDataReadWrite = (session: Session, command: number, data: DataView | null): DataView => { + const DRW_HEADER_LEN = 0x10; + const TOKEN_LEN = 0x4; + const CHANNEL = 0; + const START_CMD = 0x110a; + + let pkt_len = DRW_HEADER_LEN + TOKEN_LEN; + let payload_len = TOKEN_LEN; + let bufCopy: Uint8Array | null = null; + if (data) { + bufCopy = new Uint8Array(data.buffer); + const bufDV = new DataView(bufCopy.buffer); + // this mutates the buffer, don't want to mutate the caller + XqBytesEnc(bufDV, bufDV.byteLength, 4); + pkt_len += bufDV.byteLength; + payload_len += bufDV.byteLength; + } + + const ret = new DataView(new Uint8Array(pkt_len).buffer); + ret.add(0).writeU16(Commands.Drw); + ret.add(2).writeU16(pkt_len - 4); // -4 as we ignore the [0xf1, 0xd0, len, len] + ret.add(4).writeU8(0xd1); // ? + ret.add(5).writeU8(CHANNEL); + ret.add(6).writeU16(session.outgoingCommandId); + ret.add(8).writeU16(START_CMD); + ret.add(10).writeU16(command); + ret.add(12).writeU16(u16_swap(payload_len)); + ret.add(14).writeU16(ccDest[command]); + ret.add(16).writeByteArray(session.ticket); + if (data) { + ret.add(20).writeByteArray(bufCopy); + } + + return ret; +}; + +export const SendDevStatus = (session: Session): DataView => { + return makeDataReadWrite(session, ControlCommands.DevStatus, null); }; export const SendStartVideo = (session: Session): DataView => { - // TODO: extract SendUsrChk - let buf = new DataView(new Uint8Array(0x18).buffer); - // console.log(hexdump(DrwHdr(0xf1d0, 0x0114, 0xd1, 0, pkt_id).buffer)); - - let bytes = [ - 0xf1, - 0xd0, - 0x01, // len? lower values= no response, larger values = 1 frame then kicked - 0x14, // len - 0xd1, // ? - 0x00, // chan - session.outgoingCommandId >> 8, - session.outgoingCommandId, - 0x11, - 0x0a, - 0x10, - 0x30, - 0x08, - 0x01, - 0x00, - 0x00, - session.ticket[0], - session.ticket[1], - session.ticket[2], - session.ticket[3], - 0x01, - 0x01, - 0x01, - 0x01, - ]; - buf.writeByteArray(bytes); - return buf; + return makeDataReadWrite(session, ControlCommands.StartVideo, null); }; -export const SendUsrChk = (username: string, password: string, pkt_id: number): DataView => { - // type is char account[0x20]; char password[0x80]; + +export const SendUsrChk = (session: Session, username: string, password: string): DataView => { let buf = new Uint8Array(0x20 + 0x80); buf.fill(0); let cmd_payload = new DataView(buf.buffer); + // type is char account[0x20]; char password[0x80]; cmd_payload.writeByteArray(str2byte(username)); cmd_payload.add(0x20).writeByteArray(str2byte(password)); - - const start = 0xa11; - const dest = 0xff; - const cmd = 0x1020; // FIXME ControlCommands.ConnectUser - const len = buf.byteLength; - let cmdHeader = CmdSndProcHdr(start, cmd, len, dest); - let ret = new DataView(new Uint8Array(12 + len).buffer); - ret.writeByteArray(new Uint8Array(cmdHeader.buffer)); - XqBytesEnc(cmd_payload, 0x20 + 0x80, 4); - ret.add(12).writeByteArray(new Uint8Array(cmd_payload.buffer)); - - // need to encapsulate this into create_Drw(outbuf, 0xd1, param4?, svar1?, - // copy_len, inbuf); seems like param4/svar1 are overflowing == maybe '0xa' - // and '0x2010'?? - let retret = DrwHdr(0xf1d0, 8 + 12 + len - 4, 0xd1, 0, pkt_id); - retret.add(8).writeByteArray(new Uint8Array(ret.buffer)); - return retret; + return makeDataReadWrite(session, ControlCommands.ConnectUser, cmd_payload); }; export const create_P2pRdy = (inbuf: DataView): DataView => { diff --git a/tests/fn.test.js b/tests/fn.test.js index 132c55b..d20b6bf 100644 --- a/tests/fn.test.js +++ b/tests/fn.test.js @@ -5,7 +5,7 @@ import assert from "assert"; import { XqBytesDec, XqBytesEnc } from "../func_replacements.js"; import { createResponseForControlCommand } from "../handlers.js"; import { hexdump } from "../hexdump.js"; -import { SendUsrChk } from "../impl.ts"; +import { SendDevStatus, SendUsrChk } from "../impl.ts"; import { placeholderTypes, sprintf } from "../utils.js"; describe("debug_tools", () => { @@ -128,12 +128,13 @@ describe("make packet", () => { const expected_str = "f1d000b0d1000000110a2010a400ff00000000006f01010101010101010101010101010101010101010101010101010160656c686f01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010160656c68"; const expected = hstrToBA(expected_str); - assert.deepEqual(SendUsrChk("admin", "admin").buffer, expected); + const sess = { outgoingCommandId: 0, ticket: [0, 0, 0, 0] }; + assert.deepEqual(SendUsrChk(sess, "admin", "admin").buffer, expected); }); it("builds a good SendStartVideo", () => { const input_pkt_str = "f1d00018d1000000110a20110c00ff000000000064504737fe010101"; // token-in = 0x64 0x50 0x47 0x37 - const _expected_str = "f1d00114d1000000110a1030080100006551463601010101"; + const _expected_str = "f1d00010d1000000110a10300400000065514636"; // output is 0x3010; 'start video'; hardcoded but shouldnt const expected = hstrToBA(_expected_str); @@ -142,4 +143,12 @@ describe("make packet", () => { assert.deepEqual(got.buffer, expected); assert.deepEqual(sess.ticket, [0x65, 0x51, 0x46, 0x36]); }); + it("builds a good SendDevStatus", () => { + const sess = { outgoingCommandId: 0, ticket: [1, 2, 3, 4] }; + const _expected_str = "f1d00010d1000000110a08100400000001020304"; + const expected = hstrToBA(_expected_str); + + const got = SendDevStatus(sess); + assert.deepEqual(got.buffer, expected); + }); });