mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-22 03:10:42 +08:00
fix: harden address parsing edge cases
This commit is contained in:
+2
-1
@@ -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
@@ -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,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
@@ -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
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user