From 5512912b2203d4b92fc02f14a91dd8f1cae52917 Mon Sep 17 00:00:00 2001 From: KebenSun Date: Sat, 17 Jan 2026 03:00:49 +0900 Subject: [PATCH] fix(worker): omit Globalping ipVersion for IP targets (#177) Globalping rejects measurementOptions.ipVersion when the measurement target is an IP address. Add an IP-target guard so ipVersion is only sent for domain targets, preventing validation errors when checking IP:port monitors. --- worker/src/monitor.ts | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/worker/src/monitor.ts b/worker/src/monitor.ts index 4ad4e77..d0ce142 100644 --- a/worker/src/monitor.ts +++ b/worker/src/monitor.ts @@ -1,6 +1,28 @@ import { MonitorTarget } from '../../types/config' import { withTimeout, fetchTimeout } from './util' +function isIpAddress(hostname: string): boolean { + // `URL.hostname` strips brackets for IPv6, so a `:` reliably indicates an IPv6 literal here. + if (hostname.includes(':')) return true + + const parts = hostname.split('.') + if (parts.length !== 4) return false + + return parts.every((part) => { + if (!/^\d{1,3}$/.test(part)) return false + const value = Number(part) + return value >= 0 && value <= 255 + }) +} + +function getDomainOnlyIpVersionOption(hostname: string, gpUrl: URL): { ipVersion?: number } { + // Globalping only allows `measurementOptions.ipVersion` when `target` is a domain (it controls DNS resolution). + if (isIpAddress(hostname)) return {} + + // Keep the original behavior for domain targets. + return { ipVersion: Number(gpUrl.searchParams.get('ipVersion') || 4) } +} + async function httpResponseBasicCheck( monitor: MonitorTarget, code: number, @@ -66,6 +88,7 @@ export async function getStatusWithGlobalPing( if (monitor.method === 'TCP_PING') { const targetUrl = new URL('https://' + monitor.target) // dummy https:// to parse hostname & port + const ipVersionOption = getDomainOnlyIpVersionOption(targetUrl.hostname, gpUrl) globalPingRequest = { type: 'ping', target: targetUrl.hostname, @@ -81,11 +104,12 @@ export async function getStatusWithGlobalPing( port: targetUrl.port, packets: 1, protocol: 'tcp', // TODO: icmp? - ipVersion: Number(gpUrl.searchParams.get('ipVersion') || 4), + ...ipVersionOption, }, } } else { const targetUrl = new URL(monitor.target) + const ipVersionOption = getDomainOnlyIpVersionOption(targetUrl.hostname, gpUrl) if (monitor.body !== undefined) { throw 'custom body not supported' } @@ -119,7 +143,7 @@ export async function getStatusWithGlobalPing( : 443 : Number(targetUrl.port), protocol: targetUrl.protocol.replace(':', ''), - ipVersion: Number(gpUrl.searchParams.get('ipVersion') || 4), + ...ipVersionOption, }, } }