mirror of
https://github.com/minescope/mineping.git
synced 2025-02-25 18:23:28 +03:00
refactor: improve Bedrock error handling and validation
This commit is contained in:
parent
c71236f223
commit
0959403b1b
@ -10,6 +10,7 @@ import crypto from "node:crypto";
|
|||||||
|
|
||||||
const MAGIC = "00ffff00fefefefefdfdfdfd12345678";
|
const MAGIC = "00ffff00fefefefefdfdfdfd12345678";
|
||||||
const START_TIME = new Date().getTime();
|
const START_TIME = new Date().getTime();
|
||||||
|
const UNCONNECTED_PONG = 0x1c;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an Unconnected Ping packet.
|
* Creates an Unconnected Ping packet.
|
||||||
@ -30,12 +31,25 @@ const createUnconnectedPingFrame = (timestamp) => {
|
|||||||
* Extract Modt from Unconnected Pong Packet and convert to an object
|
* Extract Modt from Unconnected Pong Packet and convert to an object
|
||||||
* @param {Buffer} unconnectedPongPacket
|
* @param {Buffer} unconnectedPongPacket
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
|
* @throws {Error} If packet is malformed or invalid
|
||||||
* @see {@link https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Raknet_Protocol#Unconnected_Pong}
|
* @see {@link https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Raknet_Protocol#Unconnected_Pong}
|
||||||
*/
|
*/
|
||||||
const extractModt = (unconnectedPongPacket) => {
|
const extractModt = (unconnectedPongPacket) => {
|
||||||
// Skip everything to Modt
|
if (
|
||||||
|
!Buffer.isBuffer(unconnectedPongPacket) ||
|
||||||
|
unconnectedPongPacket.length < 35
|
||||||
|
) {
|
||||||
|
throw new Error("Invalid pong packet");
|
||||||
|
}
|
||||||
|
|
||||||
const offset = 33;
|
const offset = 33;
|
||||||
const length = unconnectedPongPacket.readUInt16BE(offset);
|
const length = unconnectedPongPacket.readUInt16BE(offset);
|
||||||
|
|
||||||
|
// Check for buffer bounds
|
||||||
|
if (offset + 2 + length > unconnectedPongPacket.length) {
|
||||||
|
throw new Error("Malformed pong packet");
|
||||||
|
}
|
||||||
|
|
||||||
let modt = unconnectedPongPacket.toString(
|
let modt = unconnectedPongPacket.toString(
|
||||||
"utf-8",
|
"utf-8",
|
||||||
offset + 2,
|
offset + 2,
|
||||||
@ -43,6 +57,12 @@ const extractModt = (unconnectedPongPacket) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const components = modt.split(";");
|
const components = modt.split(";");
|
||||||
|
|
||||||
|
// Validate required components
|
||||||
|
if (components.length < 9) {
|
||||||
|
throw new Error("Invalid MODT format");
|
||||||
|
}
|
||||||
|
|
||||||
const parsedComponents = {
|
const parsedComponents = {
|
||||||
edition: components[0],
|
edition: components[0],
|
||||||
name: components[1],
|
name: components[1],
|
||||||
@ -109,20 +129,23 @@ const ping = (host, port = 19132, cb, timeout = 5000) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
socket.on("message", (pongPacket) => {
|
socket.on("message", (pongPacket) => {
|
||||||
|
if (!Buffer.isBuffer(pongPacket) || pongPacket.length === 0) {
|
||||||
|
handleError(new Error("Invalid packet received"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const id = pongPacket[0];
|
const id = pongPacket[0];
|
||||||
|
if (id !== UNCONNECTED_PONG) {
|
||||||
|
handleError(new Error(`Unexpected packet ID: 0x${id.toString(16)}`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (id) {
|
try {
|
||||||
case 0x1c: {
|
const modtObject = extractModt(pongPacket);
|
||||||
const modtObject = extractModt(pongPacket);
|
closeSocket();
|
||||||
closeSocket();
|
cb(modtObject, null);
|
||||||
cb(modtObject, null);
|
} catch (err) {
|
||||||
break;
|
handleError(err);
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
handleError(new Error("Received unexpected packet"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ const varint = {
|
|||||||
buf.writeUInt8(byte | 0x80, written++);
|
buf.writeUInt8(byte | 0x80, written++);
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf.slice(0, written);
|
return buf.subarray(0, written);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
68
types/lib/bedrock.d.ts
vendored
68
types/lib/bedrock.d.ts
vendored
@ -1,53 +1,61 @@
|
|||||||
/**
|
/**
|
||||||
* @param port The server port.
|
* @param port The server port (1-65535).
|
||||||
* @param timeout The read/write socket timeout.
|
* @param timeout The read/write socket timeout in milliseconds.
|
||||||
*/
|
*/
|
||||||
export type BedrockPingOptions = {
|
export type BedrockPingOptions = {
|
||||||
port?: number;
|
port?: number & { _brand: "Port" }; // 1-65535
|
||||||
timeout?: number;
|
timeout?: number & { _brand: "Timeout" }; // > 0
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BedrockPingResponse = {
|
export type BedrockPingResponse = {
|
||||||
edition: string;
|
edition: string;
|
||||||
name: string;
|
name: string;
|
||||||
version: {
|
version: {
|
||||||
protocolVersion: number;
|
protocolVersion: number;
|
||||||
minecraftVersion: string;
|
minecraftVersion: string;
|
||||||
};
|
};
|
||||||
players: {
|
players: {
|
||||||
online: number;
|
online: number;
|
||||||
max: number;
|
max: number;
|
||||||
};
|
};
|
||||||
serverId: string;
|
serverId: string;
|
||||||
mapName: string;
|
mapName: string;
|
||||||
gameMode: string;
|
gameMode: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asynchronously ping Minecraft Bedrock server.
|
* Asynchronously ping Minecraft Bedrock server.
|
||||||
*
|
*
|
||||||
* The optional `options` argument can be an object with a `ping` (default is `19132`) or/and `timeout` (default is `5000`) property.
|
|
||||||
*
|
|
||||||
* @param host The Bedrock server address.
|
* @param host The Bedrock server address.
|
||||||
* @param options The configuration for pinging Minecraft Bedrock server.
|
* @param options The configuration for pinging Minecraft Bedrock server.
|
||||||
*
|
*
|
||||||
* ```js
|
* ```js
|
||||||
* import { pingBedrock } from '@minescope/mineping';
|
* import { pingBedrock } from '@minescope/mineping';
|
||||||
*
|
*
|
||||||
* const data = await pingBedrock('mco.mineplex.com');
|
* const data = await pingBedrock('mco.mineplex.com');
|
||||||
* console.log(data);
|
* console.log(data);
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* The resulting output will resemble:
|
* The resulting output will resemble:
|
||||||
* ```console
|
* ```console
|
||||||
* {
|
* {
|
||||||
* version: { name: 'Mineplex', protocol: '475' },
|
* edition: "MCPE",
|
||||||
* players: { max: '5207', online: '5206' },
|
* name: "Mineplex",
|
||||||
* description: ' New Costumes',
|
* version: {
|
||||||
* gamemode: 'Survival'
|
* protocolVersion: 475,
|
||||||
|
* minecraftVersion: "1.18.0"
|
||||||
|
* },
|
||||||
|
* players: {
|
||||||
|
* online: 5206,
|
||||||
|
* max: 5207
|
||||||
|
* },
|
||||||
|
* serverId: "12345678",
|
||||||
|
* mapName: "Lobby",
|
||||||
|
* gameMode: "Survival"
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
* @see [source](https://github.com/minescope/mineping/blob/915edbec9c9ad811459458600af3531ec0836911/lib/bedrock.js#L204)
|
|
||||||
*/
|
*/
|
||||||
export function pingBedrock(host: string, options?: BedrockPingOptions): Promise<BedrockPingResponse>;
|
export function pingBedrock(
|
||||||
|
host: string,
|
||||||
|
options?: BedrockPingOptions
|
||||||
|
): Promise<BedrockPingResponse>;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user