diff --git a/backend/internal/siteMonitor/monitor.go b/backend/internal/siteMonitor/monitor.go index 6965423..02995fc 100644 --- a/backend/internal/siteMonitor/monitor.go +++ b/backend/internal/siteMonitor/monitor.go @@ -8,6 +8,7 @@ import ( "net/http" "strings" "time" + "strconv" ) // SSLInfo 定义结果结构体 @@ -188,18 +189,48 @@ func UpdInfo(id, domain string, s *public.Sqlite, reportType string) error { return errCheck } +// ExtractHostPort 从 target 中提取 host 和 port 并校验,若无 port 则使用默认值 +func ExtractAndValidateHostPort(target, defaultPort string) (host string, port string, err error) { + host = target + port = defaultPort + + // 处理带端口情况 + if strings.Contains(target, ":") { + h, p, splitErr := net.SplitHostPort(target) + if splitErr == nil { + host, port = h, p + } else if !(strings.Count(target, ":") >= 2) || strings.Contains(target, "]") { + return "", "", fmt.Errorf("地址格式错误:%v", splitErr) + } + } + + // 验证端口是数字且在有效范围内 + portNum, convErr := strconv.Atoi(port) + if convErr != nil || portNum < 1 || portNum > 65535 { + return "", "", fmt.Errorf("端口号非法:%s", port) + } + + return host, port, nil +} + // CheckWebsite 实际检测函数 func CheckWebsite(target string) (*SSLInfo, error) { result := &SSLInfo{Target: target} + host, port, err := ExtractAndValidateHostPort(target, "443") + + if err != nil { + return nil, err + } + // 验证格式是否是 IP 或域名 - if net.ParseIP(target) == nil { - if _, err := net.LookupHost(target); err != nil { + if net.ParseIP(host) == nil { + if _, err := net.LookupHost(host); err != nil { return result, fmt.Errorf("无效域名或 IP:%v", err) } } - hostPort := net.JoinHostPort(target, "443") + hostPort := net.JoinHostPort(host, port) // result := &SSLInfo{Target: target} @@ -213,7 +244,7 @@ func CheckWebsite(target string) (*SSLInfo, error) { defer conn.Close() // 发送 HTTPS 请求检测状态 - resp, err := http.Get("https://" + target) + resp, err := http.Get("https://" + hostPort) if err != nil { result.HTTPStatus = 0 result.HTTPStatusText = "异常" diff --git a/backend/internal/siteMonitor/monitor_test.go b/backend/internal/siteMonitor/monitor_test.go index a360309..b1ad0ff 100644 --- a/backend/internal/siteMonitor/monitor_test.go +++ b/backend/internal/siteMonitor/monitor_test.go @@ -6,7 +6,7 @@ import ( ) func Test(t *testing.T) { - site := "bt.cn" // 只传域名或 IP,不要 http:// + site := "bt.cn:443" // 只传域名或 IP,不要 http:// result, err := CheckWebsite(site) if err != nil { fmt.Printf("❌ 检测失败: %v\n", err) diff --git a/frontend/apps/allin-ssl/src/views/monitor/useController.tsx b/frontend/apps/allin-ssl/src/views/monitor/useController.tsx index a5e7198..296835e 100644 --- a/frontend/apps/allin-ssl/src/views/monitor/useController.tsx +++ b/frontend/apps/allin-ssl/src/views/monitor/useController.tsx @@ -14,7 +14,7 @@ import { useLoadingMask, } from '@baota/naive-ui/hooks' import { useError } from '@baota/hooks/error' -import { isDomain } from '@baota/utils/business' +import { isDomain, isIp, isPort } from '@baota/utils/business' import { $t } from '@locales/index' // Store和组件 @@ -335,6 +335,61 @@ export const useMonitorFormController = (data: UpdateSiteMonitorParams | null = }), ]) + /** + * 验证输入是否合法域名或 IP 地址 + */ + function isValidHost(host: string): boolean { + if (!host?.trim()) return false; + + const trimmedHost = host.trim(); + let hostPart: string; + let portPart: string | undefined; + + // 分离主机和端口部分 + if (trimmedHost.startsWith('[')) { + // IPv6 地址(可能带端口) + const closingBracketIndex = trimmedHost.indexOf(']'); + // 缺少闭合括号 + if (closingBracketIndex === -1) { + return false; + } + // 去掉 [] + hostPart = trimmedHost.slice(1, closingBracketIndex); + const rest = trimmedHost.slice(closingBracketIndex + 1); + + // 检查剩余部分(只能是 :端口 或空) + if (rest) { + // 非端口部分 + if (!rest.startsWith(':')) { + return false; + } + portPart = rest.slice(1); + } + } else { + // IPv4 或域名(可能带端口) + const lastColonIndex = trimmedHost.lastIndexOf(':'); + if (lastColonIndex !== -1) { + hostPart = trimmedHost.slice(0, lastColonIndex); + portPart = trimmedHost.slice(lastColonIndex + 1); + } else { + hostPart = trimmedHost; + } + } + + // 检查主机部分(IPv4/IPv6/域名) + const isHostValid = isIp(hostPart) || isDomain(hostPart); + if (!isHostValid) { + return false; + } + + // 检查端口部分 + if (portPart !== undefined) { + return isPort(portPart); + } + + return true; + } + /** * 表单验证规则 */ @@ -345,7 +400,7 @@ export const useMonitorFormController = (data: UpdateSiteMonitorParams | null = message: '请输入正确的域名', trigger: 'input', validator: (rule: any, value: any, callback: any) => { - if (!isDomain(value)) { + if (!isValidHost(value)) { callback(new Error('请输入正确的域名')) } else { callback()