mirror of
https://github.com/minescope/mineping.git
synced 2025-07-03 06:28:17 +03:00
feat: skip SRV lookup for IP addresses and localhost
Performing DNS SRV lookups for hosts that are already direct IP addresses or special-case hostnames like 'localhost' is unnecessary. In addition, several related test suite improvements have been included: - A bug in the Bedrock mock packet creation was fixed where the magic GUID was being written to an incorrect buffer offset. - The DNS mocking strategy in the Java tests has been refactored for better accuracy by mocking the `dns.promises.Resolver` class. - An error test for the Bedrock pinger was corrected to properly handle a promise rejection instead of a synchronous throw.
This commit is contained in:
parent
1e6b5b3973
commit
371fb9daa7
27
lib/java.js
27
lib/java.js
@ -5,7 +5,7 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
import net from "node:net";
|
||||
import { createConnection, isIP } from "node:net";
|
||||
import { Resolver } from "node:dns/promises";
|
||||
import createDebug from "debug";
|
||||
import * as varint from "./varint.js";
|
||||
@ -147,6 +147,22 @@ export async function pingJava(host, options = {}) {
|
||||
let targetHost = host;
|
||||
let targetPort = fallbackPort;
|
||||
|
||||
// A list of hostnames that should never have an SRV lookup.
|
||||
const nonSrvLookableHostnames = ["localhost"];
|
||||
|
||||
// Check if the host is a valid IP address (v4 or v6).
|
||||
// net.isIP() returns 0 for invalid IPs, 4 for IPv4, and 6 for IPv6.
|
||||
const isDirectIp = isIP(host) !== 0;
|
||||
const isNonLookableHostname = nonSrvLookableHostnames.includes(
|
||||
host.toLowerCase()
|
||||
);
|
||||
|
||||
if (isDirectIp || isNonLookableHostname) {
|
||||
debug(
|
||||
"host '%s' is a direct IP or a non-lookable hostname, skipping SRV lookup.",
|
||||
host
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
debug(
|
||||
"attempting SRV lookup for _minecraft._tcp.%s with %dms timeout",
|
||||
@ -158,7 +174,11 @@ export async function pingJava(host, options = {}) {
|
||||
if (srvRecords.length > 0) {
|
||||
targetHost = srvRecords[0].name;
|
||||
targetPort = srvRecords[0].port;
|
||||
debug("SRV lookup successful, new target: %s:%d", targetHost, targetPort);
|
||||
debug(
|
||||
"SRV lookup successful, new target: %s:%d",
|
||||
targetHost,
|
||||
targetPort
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
// Common errors like ENODATA, ENOTFOUND, or a DNS timeout (ETIMEOUT) are expected
|
||||
@ -176,10 +196,11 @@ export async function pingJava(host, options = {}) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
debug("creating TCP connection to %s:%d", targetHost, targetPort);
|
||||
const socket = net.createConnection({ host: targetHost, port: targetPort });
|
||||
const socket = createConnection({ host: targetHost, port: targetPort });
|
||||
|
||||
// Prevent cleanup tasks from running more than once
|
||||
// in case of multiple error callbacks
|
||||
|
@ -125,8 +125,10 @@ describe("bedrock.js", () => {
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
it("should throw an error if host is not provided", () => {
|
||||
expect(() => pingBedrock(null)).toThrow("Host argument is required");
|
||||
it("should throw an error if host is not provided", async () => {
|
||||
await expect(pingBedrock(null)).rejects.toThrow(
|
||||
"Host argument is required"
|
||||
);
|
||||
});
|
||||
|
||||
it("should reject on socket timeout", async () => {
|
||||
@ -167,7 +169,7 @@ function createMockPongPacket(motd) {
|
||||
const packet = Buffer.alloc(35 + motdBuffer.length);
|
||||
packet.writeUInt8(0x1c, 0);
|
||||
packet.writeBigInt64LE(BigInt(Date.now()), 1);
|
||||
Buffer.from("00ffff00fefefefefdfdfdfd12345678", "hex").copy(packet, 17);
|
||||
Buffer.from("00ffff00fefefefefdfdfdfd12345678", "hex").copy(packet, 9);
|
||||
packet.writeUInt16BE(motdBuffer.length, 33);
|
||||
motdBuffer.copy(packet, 35);
|
||||
return packet;
|
||||
|
@ -1,18 +1,25 @@
|
||||
import net from "node:net";
|
||||
import dns from "node:dns/promises";
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { pingJava } from "../lib/java.js";
|
||||
import * as varint from "../lib/varint.js";
|
||||
|
||||
const mockResolveSrv = vi.fn();
|
||||
|
||||
vi.mock("node:net");
|
||||
vi.mock("node:dns/promises");
|
||||
vi.mock("node:dns/promises", () => ({
|
||||
Resolver: vi.fn().mockImplementation(() => ({
|
||||
resolveSrv: mockResolveSrv,
|
||||
})),
|
||||
}));
|
||||
|
||||
describe("pingJava", () => {
|
||||
let mockSocket;
|
||||
|
||||
beforeEach(() => {
|
||||
// Simulate no SRV record found.
|
||||
dns.resolveSrv.mockResolvedValue([]);
|
||||
// Reset mocks before each test.
|
||||
mockResolveSrv.mockClear();
|
||||
// Simulate no SRV record found by default.
|
||||
mockResolveSrv.mockResolvedValue([]);
|
||||
|
||||
const mockHandlers = {};
|
||||
mockSocket = {
|
||||
@ -44,7 +51,11 @@ describe("pingJava", () => {
|
||||
// Allow the async SRV lookup to complete
|
||||
await vi.runAllTicks();
|
||||
|
||||
expect(dns.resolveSrv).toHaveBeenCalledWith(`_minecraft._tcp.${host}`);
|
||||
expect(net.createConnection).toHaveBeenCalledWith({
|
||||
host: host,
|
||||
port: options.port,
|
||||
});
|
||||
|
||||
expect(net.createConnection).toHaveBeenCalledWith({
|
||||
host,
|
||||
port: options.port,
|
||||
|
Loading…
x
Reference in New Issue
Block a user