修复凭证测试器计数器、消除panic、补齐i18n

- credential_tester: testCredentialWithRetry返回ErrorType,修复网络错误计数器永久不递增的bug
- scanner: os.Exit(1)改为return,defer Cleanup可正常执行
- probe_parser: 5处panic改为error返回,调用链透传到init()
- common库: parsers/initialize/network/session共17处硬编码中文改用i18n
- services插件: 18个文件115处硬编码中文改用i18n
- locale: 补齐service/parser/network相关~25个中英文键
This commit is contained in:
ZacharyZcR
2026-05-18 04:55:20 +08:00
parent c266912dcb
commit 77827bef66
33 changed files with 221 additions and 100 deletions
+52
View File
@@ -213,6 +213,36 @@ parse_error_empty_input:
other: "Input parameters are empty"
parse_error_parser_not_init:
other: "Parser not initialized"
parser_read_hosts_failed:
other: "Failed to read hosts file"
parser_parse_host_failed:
other: "Failed to parse host"
parser_parse_exclude_failed:
other: "Failed to parse exclude hosts"
parser_no_valid_hosts:
other: "No valid hosts found"
parser_cidr_failed:
other: "CIDR parse failed {{.Arg1}}"
parser_ip_range_failed:
other: "IP range parse failed {{.Arg1}}"
parser_invalid_ip_range_fmt:
other: "Invalid IP range format: {{.Arg1}}"
parser_invalid_start_ip:
other: "Invalid start IP: {{.Arg1}}"
parser_invalid_end_ip:
other: "Invalid end IP: {{.Arg1}}"
parser_invalid_ip_end_val:
other: "Invalid IP range end value: {{.Arg1}}"
parser_invalid_ip_range_val:
other: "Invalid IP range: {{.Arg1}}-{{.Arg2}}"
parser_invalid_ip_fmt:
other: "Invalid IP address format: {{.Arg1}}"
parser_ipv4_only:
other: "IPv4 addresses only"
parser_start_gt_end:
other: "Start IP greater than end IP"
network_rate_limited:
other: "Rate limited: {{.Arg1}}"
target_local_mode:
other: "Local scan mode"
param_conflict_ao_icmp_both:
@@ -817,3 +847,25 @@ web_shutting_down:
other: "Web server shutting down..."
web_mode_not_supported:
other: "Web mode not supported in this build, rebuild with: go build -tags web"
# ========================= Service Plugin Common Messages =========================
service_no_credentials:
other: "No available test credentials"
service_not_identified:
other: "Cannot identify as {{.Arg1}} service"
service_port_restriction:
other: "{{.Arg1}} plugin only supports port {{.Arg2}}"
service_target_unreachable:
other: "Target unreachable: {{.Arg1}}"
service_unauthorized:
other: "Unauthorized access"
service_auth_failed:
other: "Authentication failed"
service_connection_failed:
other: "Connection failed: {{.Arg1}}"
service_no_weak_pass:
other: "No weak password found"
service_no_test_creds:
other: "No credentials to test"
service_conn_port_failed:
other: "Port connection failed: {{.Arg1}}"
+54
View File
@@ -213,6 +213,36 @@ parse_error_empty_input:
other: "输入参数为空"
parse_error_parser_not_init:
other: "解析器未初始化"
parser_read_hosts_failed:
other: "读取主机文件失败"
parser_parse_host_failed:
other: "解析主机失败"
parser_parse_exclude_failed:
other: "解析排除主机失败"
parser_no_valid_hosts:
other: "没有找到有效的主机"
parser_cidr_failed:
other: "CIDR解析失败 {{.Arg1}}"
parser_ip_range_failed:
other: "IP范围解析失败 {{.Arg1}}"
parser_invalid_ip_range_fmt:
other: "无效的IP范围格式: {{.Arg1}}"
parser_invalid_start_ip:
other: "无效的起始IP地址: {{.Arg1}}"
parser_invalid_end_ip:
other: "无效的结束IP地址: {{.Arg1}}"
parser_invalid_ip_end_val:
other: "无效的IP范围结束值: {{.Arg1}}"
parser_invalid_ip_range_val:
other: "无效的IP范围: {{.Arg1}}-{{.Arg2}}"
parser_invalid_ip_fmt:
other: "无效的IP地址格式: {{.Arg1}}"
parser_ipv4_only:
other: "仅支持IPv4地址范围"
parser_start_gt_end:
other: "起始IP大于结束IP"
network_rate_limited:
other: "发包受限: {{.Arg1}}"
target_local_mode:
other: "本地扫描模式"
param_conflict_ao_icmp_both:
@@ -796,6 +826,8 @@ webscan_response_parse_failed:
# Main 入口
param_error:
other: "参数错误: {{.Arg1}}"
param_exclusive:
other: "参数 {{.Arg1}} 互斥,请只指定一个扫描目标\n -h: 网络主机扫描\n -u: Web URL扫描\n -local: 本地信息收集"
error_generic:
other: "错误: {{.Arg1}}"
init_failed:
@@ -814,3 +846,25 @@ web_shutting_down:
other: "Web服务器正在关闭..."
web_mode_not_supported:
other: "当前版本不支持Web模式,请使用 -tags web 重新编译"
# ========================= 服务插件通用消息 =========================
service_no_credentials:
other: "没有可用的测试凭据"
service_not_identified:
other: "无法识别为{{.Arg1}}服务"
service_port_restriction:
other: "{{.Arg1}}插件仅支持{{.Arg2}}端口"
service_target_unreachable:
other: "目标不可达: {{.Arg1}}"
service_unauthorized:
other: "未授权访问"
service_auth_failed:
other: "认证失败"
service_connection_failed:
other: "连接失败: {{.Arg1}}"
service_no_weak_pass:
other: "未发现弱密码"
service_no_test_creds:
other: "无凭据可测试"
service_conn_port_failed:
other: "连接端口失败: {{.Arg1}}"
+3 -1
View File
@@ -2,6 +2,8 @@ package common
import (
"fmt"
"github.com/shadow1ng/fscan/common/i18n"
)
/*
@@ -80,7 +82,7 @@ func ValidateExclusiveParams(info *HostInfo) error {
}
if paramCount > 1 {
return fmt.Errorf("参数 %s 互斥,请只指定一个扫描目标\n -h: 网络主机扫描\n -u: Web URL扫描\n -local: 本地信息收集", activeParam)
return fmt.Errorf(i18n.Tr("param_exclusive", activeParam))
}
return nil
+3 -2
View File
@@ -17,6 +17,7 @@ import (
"time"
"github.com/shadow1ng/fscan/common/proxy"
"github.com/shadow1ng/fscan/common/i18n"
)
// =============================================================================
@@ -109,7 +110,7 @@ func WrapperTcpWithTimeout(network, address string, timeout time.Duration) (net.
// 检查发包限制 - 在代理连接前进行控制
if canSend, reason := CanSendPacket(); !canSend {
LogError(fmt.Sprintf("TCP连接 %s 受限: %s", address, reason))
return nil, fmt.Errorf("发包受限: %s", reason)
return nil, fmt.Errorf(i18n.Tr("network_rate_limited", reason))
}
// 获取全局拨号器(复用,避免重复创建)
@@ -166,7 +167,7 @@ func SafeHTTPDo(client *http.Client, req *http.Request) (*http.Response, error)
// 检查发包限制
if canSend, reason := CanSendPacket(); !canSend {
LogError(fmt.Sprintf("HTTP请求 %s 受限: %s", req.URL.String(), reason))
return nil, fmt.Errorf("发包受限: %s", reason)
return nil, fmt.Errorf(i18n.Tr("network_rate_limited", reason))
}
// 执行HTTP请求
+15 -14
View File
@@ -11,6 +11,7 @@ import (
"strings"
"github.com/shadow1ng/fscan/common/config"
"github.com/shadow1ng/fscan/common/i18n"
)
/*
@@ -37,7 +38,7 @@ func ParseIP(host string, filename string, nohosts ...string) ([]string, error)
if filename != "" {
fileHosts, err := ReadLinesFromFile(filename)
if err != nil {
return nil, fmt.Errorf("读取主机文件失败: %w", err)
return nil, fmt.Errorf(i18n.GetText("parser_read_hosts_failed")+": %w", err)
}
for _, h := range fileHosts {
parsed, err := parseHostString(h)
@@ -52,7 +53,7 @@ func ParseIP(host string, filename string, nohosts ...string) ([]string, error)
if host != "" {
hostList, err := parseHostString(host)
if err != nil {
return nil, fmt.Errorf("解析主机失败: %w", err)
return nil, fmt.Errorf(i18n.GetText("parser_parse_host_failed")+": %w", err)
}
hosts = append(hosts, hostList...)
}
@@ -61,7 +62,7 @@ func ParseIP(host string, filename string, nohosts ...string) ([]string, error)
if len(nohosts) > 0 && nohosts[0] != "" {
excludeList, err := parseHostString(nohosts[0])
if err != nil {
return nil, fmt.Errorf("解析排除主机失败: %w", err)
return nil, fmt.Errorf(i18n.GetText("parser_parse_exclude_failed")+": %w", err)
}
hosts = excludeFromList(hosts, excludeList)
}
@@ -71,7 +72,7 @@ func ParseIP(host string, filename string, nohosts ...string) ([]string, error)
sort.Strings(hosts)
if len(hosts) == 0 {
return nil, fmt.Errorf("没有找到有效的主机")
return nil, fmt.Errorf(i18n.GetText("parser_no_valid_hosts"))
}
return hosts, nil
@@ -109,13 +110,13 @@ func parseHostString(host string) ([]string, error) {
case strings.Contains(h, "/"):
cidrHosts, err := parseIPCIDR(h, SimpleMaxHosts)
if err != nil {
return nil, fmt.Errorf("CIDR解析失败 %s: %w", h, err)
return nil, fmt.Errorf(i18n.Tr("parser_cidr_failed", h)+": %w", err)
}
hosts = append(hosts, cidrHosts...)
case strings.Contains(h, "-") && !strings.Contains(h, ":") && looksLikeIPRange(h):
rangeHosts, err := parseIPRangeString(h, SimpleMaxHosts)
if err != nil {
return nil, fmt.Errorf("IP范围解析失败 %s: %w", h, err)
return nil, fmt.Errorf(i18n.Tr("parser_ip_range_failed", h)+": %w", err)
}
hosts = append(hosts, rangeHosts...)
default:
@@ -333,7 +334,7 @@ func looksLikeIPRange(s string) bool {
func parseIPRangeString(rangeStr string, maxTargets int) ([]string, error) {
parts := strings.Split(rangeStr, "-")
if len(parts) != 2 {
return nil, fmt.Errorf("无效的IP范围格式: %s", rangeStr)
return nil, fmt.Errorf(i18n.Tr("parser_invalid_ip_range_fmt", rangeStr))
}
startIPStr := strings.TrimSpace(parts[0])
@@ -341,7 +342,7 @@ func parseIPRangeString(rangeStr string, maxTargets int) ([]string, error) {
startIP := net.ParseIP(startIPStr)
if startIP == nil {
return nil, fmt.Errorf("无效的起始IP地址: %s", startIPStr)
return nil, fmt.Errorf(i18n.Tr("parser_invalid_start_ip", startIPStr))
}
// 处理简写格式 (如: 192.168.1.1-100)
@@ -352,7 +353,7 @@ func parseIPRangeString(rangeStr string, maxTargets int) ([]string, error) {
// 处理完整格式 (如: 192.168.1.1-192.168.1.100)
endIP := net.ParseIP(endIPStr)
if endIP == nil {
return nil, fmt.Errorf("无效的结束IP地址: %s", endIPStr)
return nil, fmt.Errorf(i18n.Tr("parser_invalid_end_ip", endIPStr))
}
return parseIPFullRange(startIP, endIP, maxTargets)
@@ -362,18 +363,18 @@ func parseIPRangeString(rangeStr string, maxTargets int) ([]string, error) {
func parseIPShortRange(startIPStr, endSuffix string, maxTargets int) ([]string, error) {
endNum, err := strconv.Atoi(endSuffix)
if err != nil || endNum > 255 {
return nil, fmt.Errorf("无效的IP范围结束值: %s", endSuffix)
return nil, fmt.Errorf(i18n.Tr("parser_invalid_ip_end_val", endSuffix))
}
ipParts := strings.Split(startIPStr, ".")
if len(ipParts) != 4 {
return nil, fmt.Errorf("无效的IP地址格式: %s", startIPStr)
return nil, fmt.Errorf(i18n.Tr("parser_invalid_ip_fmt", startIPStr))
}
prefixIP := strings.Join(ipParts[0:3], ".")
startNum, err := strconv.Atoi(ipParts[3])
if err != nil || startNum > endNum {
return nil, fmt.Errorf("无效的IP范围: %s-%s", startIPStr, endSuffix)
return nil, fmt.Errorf(i18n.Tr("parser_invalid_ip_range_val", startIPStr, endSuffix))
}
var allIP []string
@@ -394,14 +395,14 @@ func parseIPFullRange(startIP, endIP net.IP, maxTargets int) ([]string, error) {
start4 := startIP.To4()
end4 := endIP.To4()
if start4 == nil || end4 == nil {
return nil, fmt.Errorf("仅支持IPv4地址范围")
return nil, fmt.Errorf(i18n.GetText("parser_ipv4_only"))
}
startInt := (int(start4[0]) << 24) | (int(start4[1]) << 16) | (int(start4[2]) << 8) | int(start4[3])
endInt := (int(end4[0]) << 24) | (int(end4[1]) << 16) | (int(end4[2]) << 8) | int(end4[3])
if startInt > endInt {
return nil, fmt.Errorf("起始IP大于结束IP")
return nil, fmt.Errorf(i18n.GetText("parser_start_gt_end"))
}
var ips []string
+2 -1
View File
@@ -9,6 +9,7 @@ import (
"time"
"github.com/shadow1ng/fscan/common/proxy"
"github.com/shadow1ng/fscan/common/i18n"
)
// ScanSession 封装单次扫描的全部上下文
@@ -38,7 +39,7 @@ func (s *ScanSession) DialTCP(ctx context.Context, network, address string, time
// 检查发包限制
if ok, err := CanSendPacketWith(s.Config, s.State); !ok {
LogError(fmt.Sprintf("TCP连接 %s 受限: %s", address, err.Error()))
return nil, fmt.Errorf("发包受限: %s", err.Error())
return nil, fmt.Errorf(i18n.Tr("network_rate_limited", err.Error()))
}
// 获取 dialer