fix: harden address parsing edge cases

This commit is contained in:
ZacharyZcR
2026-06-01 04:03:46 +08:00
parent 8ec96bfe6d
commit 569d21a8bc
43 changed files with 273 additions and 137 deletions
+2 -1
View File
@@ -8,6 +8,7 @@ import (
"net"
"os/exec"
"runtime"
"strconv"
"strings"
"sync"
"sync/atomic"
@@ -704,7 +705,7 @@ func tcpProbeAlive(ctx context.Context, session *common.ScanSession, host string
result := make(chan bool, len(tcpProbeCommonPorts))
for _, port := range tcpProbeCommonPorts {
go func(p int) {
addr := fmt.Sprintf("%s:%d", host, p)
addr := net.JoinHostPort(host, strconv.Itoa(p))
conn, err := session.DialTCP(ctx, "tcp", addr, tcpProbeTimeout)
if err == nil {
_ = conn.Close()
+5 -4
View File
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net"
"strconv"
"strings"
"sync"
"sync/atomic"
@@ -673,7 +674,7 @@ func processServiceResult(ctx context.Context, host string, port int, addr strin
_ = session.SaveResult(&output.ScanResult{
Time: time.Now(),
Type: output.TypeService,
Target: fmt.Sprintf("%s:%d", host, port),
Target: net.JoinHostPort(host, strconv.Itoa(port)),
Status: "identified",
Details: details,
})
@@ -741,7 +742,7 @@ func tryHTTPFallbackDetection(ctx context.Context, host string, port int, addr s
_ = session.SaveResult(&output.ScanResult{
Time: time.Now(),
Type: output.TypeService,
Target: fmt.Sprintf("%s:%d", host, port),
Target: net.JoinHostPort(host, strconv.Itoa(port)),
Status: "identified",
Details: details,
})
@@ -812,7 +813,7 @@ func probeSubnets(ctx context.Context, hosts []string, timeout time.Duration, se
_ = conn.Close()
aliveSubnets.Store(pfx, true)
}
}(prefix, fmt.Sprintf("%s:%d", gw, port))
}(prefix, net.JoinHostPort(gw, strconv.Itoa(port)))
}
}
}
@@ -845,7 +846,7 @@ func probeSubnets(ctx context.Context, hosts []string, timeout time.Duration, se
go func(pfx, h string, p int) {
defer func() { <-limiter; wg.Done() }()
conn, err := session.DialTCP(ctx, "tcp", fmt.Sprintf("%s:%d", h, p), subnetProbeTimeout)
conn, err := session.DialTCP(ctx, "tcp", net.JoinHostPort(h, strconv.Itoa(p)), subnetProbeTimeout)
if err == nil {
_ = conn.Close()
aliveSubnets.Store(pfx, true)
+3 -3
View File
@@ -3,9 +3,9 @@ package core
import (
"context"
"errors"
"fmt"
"io"
"net"
"strconv"
"strings"
"sync"
"time"
@@ -264,7 +264,7 @@ func (s *SmartPortInfoScanner) reconnectIfNeeded() {
}
// 重新建立连接
newConn, err := s.session.DialTCP(s.info.ctx, "tcp", fmt.Sprintf("%s:%d", s.Address, s.Port), s.Timeout)
newConn, err := s.session.DialTCP(s.info.ctx, "tcp", net.JoinHostPort(s.Address, strconv.Itoa(s.Port)), s.Timeout)
if err != nil {
return
}
@@ -542,7 +542,7 @@ func (i *Info) Write(msg []byte) error {
_ = oldConn.Close()
// 尝试重新连接 - 支持SOCKS5代理
newConn, retryErr := i.session.DialTCP(i.ctx, "tcp", fmt.Sprintf("%s:%d", i.Address, i.Port), time.Duration(6)*time.Second)
newConn, retryErr := i.session.DialTCP(i.ctx, "tcp", net.JoinHostPort(i.Address, strconv.Itoa(i.Port)), time.Duration(6)*time.Second)
if retryErr != nil {
return retryErr
}
+11 -4
View File
@@ -3,6 +3,7 @@ package core
import (
"context"
"fmt"
"net"
"strconv"
"strings"
"sync"
@@ -422,15 +423,21 @@ func (s *ServiceScanStrategy) convertToTargetInfos(ports []string, baseInfo comm
var infos []common.HostInfo
for _, targetIP := range ports {
hostParts := strings.Split(targetIP, ":")
if len(hostParts) != 2 {
targetIP = strings.TrimSpace(targetIP)
host, portStr, err := net.SplitHostPort(targetIP)
if err != nil && strings.Count(targetIP, ":") == 1 {
parts := strings.SplitN(targetIP, ":", 2)
host, portStr = parts[0], parts[1]
err = nil
}
if err != nil {
common.LogError(i18n.Tr("invalid_target_format", targetIP))
continue
}
// 去除空格并过滤空值
host := strings.TrimSpace(hostParts[0])
portStr := strings.TrimSpace(hostParts[1])
host = strings.TrimSpace(host)
portStr = strings.TrimSpace(portStr)
if host == "" || portStr == "" {
common.LogError(i18n.Tr("invalid_target_format", targetIP))
continue
+16 -2
View File
@@ -545,12 +545,26 @@ func TestConvertToTargetInfos(t *testing.T) {
},
},
{
name: "IPv6地址",
name: "IPv6地址缺少方括号",
ports: []string{"::1:8080"},
baseInfo: common.HostInfo{},
expectedLen: 0, // Split会产生多个部分,被判定为非法
expectedLen: 0,
validateFunc: nil,
},
{
name: "IPv6地址",
ports: []string{"[2001:db8::1]:8080"},
baseInfo: common.HostInfo{},
expectedLen: 1,
validateFunc: func(t *testing.T, infos []common.HostInfo) {
if infos[0].Host != "2001:db8::1" {
t.Errorf("Host = %q, 期望 '2001:db8::1'", infos[0].Host)
}
if infos[0].Port != 8080 {
t.Errorf("Port = %d, 期望 8080", infos[0].Port)
}
},
},
{
name: "域名+端口",
ports: []string{"example.com:80", "test.local:443"},
+5 -10
View File
@@ -38,7 +38,7 @@ func DetectHTTPSchemeContext(ctx context.Context, host string, port int, config
}
timeout := config.Network.WebTimeout
addr := fmt.Sprintf("%s:%d", host, port)
addr := net.JoinHostPort(host, strconv.Itoa(port))
// 第一步:尝试标准TLS握手(优先检测HTTPS)
tlsDialer := &net.Dialer{Timeout: timeout}
@@ -179,15 +179,10 @@ func isPortReachable(ctx context.Context, host string, port int, config *common.
// tryHTTP 尝试HTTP请求 - 简化的核心逻辑
func (w *WebPortDetector) tryHTTP(ctx context.Context, client *http.Client, session *common.ScanSession, host string, port int, protocol string) bool {
// 构造URL
var url string
if (port == 80 && protocol == "http") || (port == 443 && protocol == "https") {
url = fmt.Sprintf("%s://%s", protocol, host)
} else {
url = fmt.Sprintf("%s://%s:%d", protocol, host, port)
}
targetURL := (&url.URL{Scheme: protocol, Host: net.JoinHostPort(host, strconv.Itoa(port))}).String()
// 发送HEAD请求
req, err := http.NewRequestWithContext(ctx, "HEAD", url, nil)
req, err := http.NewRequestWithContext(ctx, "HEAD", targetURL, nil)
if err != nil {
return false
}
@@ -266,7 +261,7 @@ func IsWebServiceByFingerprint(serviceInfo *ServiceInfo) bool {
// MarkAsWebService 标记Web服务 - 保持API兼容
func MarkAsWebService(host string, port int, serviceInfo *ServiceInfo) {
cacheKey := fmt.Sprintf("%s:%d", host, port)
cacheKey := net.JoinHostPort(host, strconv.Itoa(port))
webCacheMutex.Lock()
defer webCacheMutex.Unlock()
@@ -276,7 +271,7 @@ func MarkAsWebService(host string, port int, serviceInfo *ServiceInfo) {
// GetWebServiceInfo 获取Web服务信息
func GetWebServiceInfo(host string, port int) (*ServiceInfo, bool) {
cacheKey := fmt.Sprintf("%s:%d", host, port)
cacheKey := net.JoinHostPort(host, strconv.Itoa(port))
webCacheMutex.RLock()
defer webCacheMutex.RUnlock()