From 342ee291917e7ef8cb6a6b6ff47f3b4c352a608a Mon Sep 17 00:00:00 2001 From: DavidVentura Date: Sat, 27 Jan 2024 13:27:28 +0100 Subject: [PATCH] move some state to session --- handlers.ts | 38 +++++++++++++++------------ impl.ts | 31 ++++++++++++---------- server.ts | 68 ++++++++++++++++++++++++++++-------------------- tests/fn.test.js | 14 +++++++++- 4 files changed, 91 insertions(+), 60 deletions(-) diff --git a/handlers.ts b/handlers.ts index 3821919..e841b60 100644 --- a/handlers.ts +++ b/handlers.ts @@ -1,19 +1,19 @@ -import { sock } from "./server.js"; +import { Session } from "./server.js"; import { ControlCommands, Commands, CommandsByValue } from "./datatypes.js"; import { createWriteStream } from "node:fs"; -import { SendUsrAck, SendUsrChk, create_P2pRdy } from "./impl.js"; +import { SendStartVideo, SendUsrChk, create_P2pRdy } from "./impl.js"; let image_fds = []; let cur_image_index = 0; let size_so_far = 0; -export const notImpl = (_: sock, dv: DataView) => { +export const notImpl = (_: Session, dv: DataView) => { const raw = dv.readU16(); const cmd = CommandsByValue[raw]; console.log(`^^ ${cmd} (${raw.toString(16)}) and it's not implemented yet`); }; -export const noop = (_: sock, __: DataView) => {}; +export const noop = (_: Session, __: DataView) => {}; const create_P2pAliveAck = (): DataView => { const outbuf = new DataView(new Uint8Array(4).buffer); outbuf.writeU16(Commands.P2PAliveAck); @@ -21,21 +21,22 @@ const create_P2pAliveAck = (): DataView => { return outbuf; }; -export const handle_P2PAlive = (sock: sock, _: DataView) => { +export const handle_P2PAlive = (session: Session, _: DataView) => { const b = create_P2pAliveAck(); - sock.send(b); + session.send(b); }; -export const handle_PunchPkt = (sock: sock, dv: DataView) => { +export const handle_PunchPkt = (session: Session, dv: DataView) => { const punchCmd = dv.readU16(); const len = dv.add(2).readU16(); const prefix = dv.add(4).readString(4); const serial = dv.add(8).readU64().toString(); const suffix = dv.add(16).readString(4); // f141 20 BATC 609531 EXLV - sock.send(create_P2pRdy(dv.add(4).readByteArray(len))); + session.send(create_P2pRdy(dv.add(4).readByteArray(len))); }; -const deal_with_control = (sock: sock, dv: DataView) => { +export const createResponseForControlCommand = (session: Session, dv: DataView): DataView | null => { + const pkt_id = dv.add(6).readU16(); const start_type = dv.add(8).readU16(); // 0xa11 on control; data starts here on DATA pkt const cmd_id = dv.add(10).readU16(); // 0x1120 @@ -58,8 +59,8 @@ const deal_with_control = (sock: sock, dv: DataView) => { challenge[1] = dv.add(0x15).readU8(); challenge[2] = dv.add(0x16).readU8(); challenge[3] = dv.add(0x17).readU8(); - const buf = SendUsrAck(challenge); - sock.send(buf); + const buf = SendStartVideo(session.outgoingCommandId, challenge); + return buf; } }; @@ -113,19 +114,22 @@ const makeDrwAck = (dv: DataView): DataView => { } return outbuf; }; -export const handle_Drw = (sock: sock, dv: DataView) => { +export const handle_Drw = (session: Session, dv: DataView) => { const ack = makeDrwAck(dv); - sock.send(ack); + session.send(ack); const m_stream = dv.add(5).readU8(); // data = 1, control = 0 if (m_stream == 1) { deal_with_data(dv); } else { - deal_with_control(sock, dv); + const b = createResponseForControlCommand(session, dv); + if (b != null) { + session.send(b); + } } }; -export const handle_P2PRdy = (sock: sock, _: DataView) => { - const b = SendUsrChk("admin", "admin"); - sock.send(b); +export const handle_P2PRdy = (session: Session, _: DataView) => { + const b = SendUsrChk("admin", "admin", session.outgoingCommandId); + session.send(b); }; diff --git a/impl.ts b/impl.ts index ca913de..17a98c9 100644 --- a/impl.ts +++ b/impl.ts @@ -1,7 +1,8 @@ import "./shim.ts"; -import { XqBytesEnc } from "./func_replacements.js"; import { Commands } from "./datatypes.js"; +import { XqBytesEnc } from "./func_replacements.js"; +import { hexdump } from "./hexdump.js"; import { u16_swap } from "./utils.js"; const str2byte = (s: string): number[] => { @@ -18,28 +19,30 @@ const CmdSndProcHdr = (start: number, cmd: number, len: number, dest: number): D return cmdHeader; }; -const DrwHdr = (cmd: number, len: number, d1_or_d2: 0xd1 | 0xd2, m_chan: number): DataView => { +const DrwHdr = (cmd: number, len: number, d1_or_d2: 0xd1 | 0xd2, m_chan: number, pkt_id: number): DataView => { let retret = new DataView(new Uint8Array(len + 4).buffer); retret.writeU16(cmd); retret.add(2).writeU16(len); // buflen -4? - retret.add(4).writeU16(u16_swap(d1_or_d2)); - retret.add(6).writeU16(m_chan); // chan? hardcoded + retret.add(4).writeU8(d1_or_d2); + retret.add(5).writeU8(m_chan); // chan? hardcoded + retret.add(6).writeU16(pkt_id); return retret; }; -export const SendUsrAck = (challenge: number[]): DataView => { +export const SendStartVideo = (pkt_id: number, challenge: number[]): DataView => { // TODO: extract SendUsrChk let buf = new DataView(new Uint8Array(0x18).buffer); - const seq = 0x1; + // console.log(hexdump(DrwHdr(0xf1d0, 0x0114, 0xd1, 0, pkt_id).buffer)); + let bytes = [ 0xf1, 0xd0, - 0x01, - 0x14, - 0xd1, - 0x00, - 0x00, - seq, + 0x01, // len? lower values= no response, larger values = 1 frame then kicked + 0x14, // len + 0xd1, // ? + 0x00, // chan + pkt_id >> 8, + pkt_id, 0x11, 0x0a, 0x10, @@ -60,7 +63,7 @@ export const SendUsrAck = (challenge: number[]): DataView => { buf.writeByteArray(bytes); return buf; }; -export const SendUsrChk = (username: string, password: string): DataView => { +export const SendUsrChk = (username: string, password: string, pkt_id: number): DataView => { // type is char account[0x20]; char password[0x80]; let buf = new Uint8Array(0x20 + 0x80); buf.fill(0); @@ -81,7 +84,7 @@ export const SendUsrChk = (username: string, password: string): DataView => { // 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); + let retret = DrwHdr(0xf1d0, 8 + 12 + len - 4, 0xd1, 0, pkt_id); retret.add(8).writeByteArray(new Uint8Array(ret.buffer)); return retret; }; diff --git a/server.ts b/server.ts index c1ef28b..79569e3 100644 --- a/server.ts +++ b/server.ts @@ -4,43 +4,42 @@ import { Commands, CommandsByValue } from "./datatypes.js"; import { handle_P2PAlive, handle_PunchPkt, handle_P2PRdy, handle_Drw, notImpl, noop } from "./handlers.js"; import { hexdump } from "./hexdump.js"; -export type sock = { - send: (msg: DataView) => void; - broadcast: (msg: DataView) => void; -}; +export type sock = {}; type opt = { debug: boolean; ansi: boolean; }; -type msgCb = (msg: Buffer, rinfo: any, options: opt) => void; -type connCb = () => void; +type msgCb = (session: Session, msg: Buffer, rinfo: any, options: opt) => void; +type connCb = (session: Session) => void; -const MakeSock = (cb: msgCb, connCb: connCb, options?: opt): sock => { - const server = dgram.createSocket("udp4"); +const makeSession = (cb: msgCb, connCb: connCb, options?: opt): Session => { + const sock = dgram.createSocket("udp4"); - server.on("error", (err) => { - console.error(`server error:\n${err.stack}`); - server.close(); + sock.on("error", (err) => { + console.error(`sock error:\n${err.stack}`); + sock.close(); }); - server.on("message", (msg, rinfo) => cb(msg, rinfo, options)); + sock.on("message", (msg, rinfo) => cb(session, msg, rinfo, options)); - server.on("listening", () => { - const address = server.address(); - console.log(`server listening ${address.address}:${address.port}`); - server.setBroadcast(true); - connCb(); + sock.on("listening", () => { + const address = sock.address(); + console.log(`sock listening ${address.address}:${address.port}`); + sock.setBroadcast(true); + connCb(session); }); const RECV_PORT = 49512; // important? const DST_IP = "192.168.1.1"; const BCAST_IP = "192.168.1.255"; const SEND_PORT = 32108; - server.bind(RECV_PORT); + sock.bind(RECV_PORT); - return { + const session: Session = { + outgoingCommandId: 0, + ticket: [0, 0, 0, 0], send: (msg: DataView) => { const raw = msg.readU16(); const cmd = CommandsByValue[raw]; @@ -48,13 +47,26 @@ const MakeSock = (cb: msgCb, connCb: connCb, options?: opt): sock => { console.log(`>> ${cmd}`); console.log(hexdump(msg.buffer, { ansi: options.ansi, ansiColor: 0 })); } - server.send(new Uint8Array(msg.buffer), SEND_PORT, DST_IP); + if (raw == Commands.Drw) { + // not sure why cmd == Commands.Drw does not work + session.outgoingCommandId++; + } + sock.send(new Uint8Array(msg.buffer), SEND_PORT, DST_IP); }, - broadcast: (msg: DataView) => server.send(new Uint8Array(msg.buffer), SEND_PORT, BCAST_IP), + broadcast: (msg: DataView) => sock.send(new Uint8Array(msg.buffer), SEND_PORT, BCAST_IP), }; + return session; }; -const Handlers: Record void> = { +export type Session = { + send: (msg: DataView) => void; + broadcast: (msg: DataView) => void; + outgoingCommandId: number; + ticket: number[]; +}; + +export type PacketHandler = (session: Session, dv: DataView) => void; +const Handlers: Record = { PunchPkt: handle_PunchPkt, Close: notImpl, @@ -80,8 +92,8 @@ const Handlers: Record void RlyHelloAck2: notImpl, // if len >1?? }; -const sock = MakeSock( - (msg, rinfo, options) => { +makeSession( + (session, msg, _, options) => { const ab = new Uint8Array(msg).buffer; const dv = new DataView(ab); const cmd = CommandsByValue[dv.readU16()]; @@ -89,14 +101,14 @@ const sock = MakeSock( console.log(`<< ${cmd}`); console.log(hexdump(msg.buffer, { ansi: options.ansi, ansiColor: 1 })); } - Handlers[cmd](sock, dv); + Handlers[cmd](session, dv); }, - () => { + (session) => { const int = setInterval(() => { let buf = new DataView(new Uint8Array(4).buffer); create_LanSearch(buf); - sock.broadcast(buf); + session.broadcast(buf); }, 1000); }, - { debug: false, ansi: false }, + { debug: true, ansi: false }, ); diff --git a/tests/fn.test.js b/tests/fn.test.js index cc9b0c3..d992d9c 100644 --- a/tests/fn.test.js +++ b/tests/fn.test.js @@ -3,9 +3,10 @@ import "../shim.ts"; 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 { placeholderTypes, sprintf } from "../utils.js"; -import { hexdump } from "../hexdump.js"; describe("debug_tools", () => { it("parses printed data", () => { @@ -122,4 +123,15 @@ describe("make packet", () => { const expected = hstrToBA(expected_str); assert.deepEqual(SendUsrChk("admin", "admin").buffer, expected); }); + it("builds a good SendStartVideo", () => { + const input_pkt_str = "f1d00018d1000000110a20110c00ff000000000064504737fe010101"; + // token-in = 0x64 0x50 0x47 0x37 + const _expected_str = "f1d00114d1000000110a1030080100006551463601010101"; + // output is 0x3010; 'start video'; hardcoded but shouldnt + const expected = hstrToBA(_expected_str); + + const sess = { outgoingCommandId: 0 }; + const got = createResponseForControlCommand(sess, new DataView(hstrToBA(input_pkt_str))); + assert.deepEqual(got.buffer, expected); + }); });