mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 05:56:46 +08:00
fix: 修复插件系统逻辑Bug和架构问题
主要修复: 1. 修复时间显示Bug - StartTime初始化问题 2. 修复Web智能探测错误检测预定义端口而非用户指定端口 3. 修复本地插件被错误调用到端口扫描中的问题 4. 修复host:port格式双重处理导致的多余端口扫描 5. 统一插件过滤逻辑,消除接口不一致性 6. 优化Web检测缓存机制,减少重复HTTP请求 技术改进: - 重构插件适用性检查逻辑,确保策略过滤器正确工作 - 区分Web检测的自动发现模式和用户指定端口模式 - 在解析阶段正确处理host:port格式,避免与默认端口冲突 - 完善缓存机制,提升性能 测试验证: - ./fscan -h 127.0.0.1:3306 现在只检测3306端口 - 本地插件不再参与端口扫描 - Web检测只对指定端口进行协议检测 - 时间显示正确
This commit is contained in:
parent
a10153911e
commit
a3177b28a6
@ -1,19 +0,0 @@
|
||||
package common
|
||||
|
||||
import "github.com/shadow1ng/fscan/common/base"
|
||||
|
||||
/*
|
||||
Ports.go - 端口常量(向后兼容层)
|
||||
|
||||
此文件保持向后兼容,实际常量定义已迁移到Core/Constants.go
|
||||
*/
|
||||
|
||||
// 向后兼容的端口常量 - 引用base包中的定义
|
||||
var (
|
||||
WebPorts = base.WebPorts // Web服务端口组
|
||||
MainPorts = base.MainPorts // 主要服务端口组
|
||||
DbPorts = base.DbPorts // 数据库端口组
|
||||
ServicePorts = base.ServicePorts // 服务端口组
|
||||
CommonPorts = base.CommonPorts // 常用端口组
|
||||
AllPorts = base.AllPorts // 全部端口
|
||||
)
|
@ -1,85 +0,0 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/shadow1ng/fscan/common/config"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// 全局配置变量
|
||||
// =============================================================================
|
||||
|
||||
var (
|
||||
// 核心扫描配置
|
||||
ScanMode string // 扫描模式
|
||||
ThreadNum int // 线程数
|
||||
Timeout int64 // 超时时间
|
||||
DisablePing bool // 禁用ping
|
||||
LocalMode bool // 本地模式
|
||||
LocalPlugin string // 本地插件选择
|
||||
|
||||
// 基础认证配置
|
||||
Username string // 用户名
|
||||
Password string // 密码
|
||||
Userdict map[string][]string // 用户字典
|
||||
Passwords []string // 密码列表
|
||||
|
||||
// 网络配置
|
||||
HttpProxy string // HTTP代理
|
||||
Socks5Proxy string // SOCKS5代理
|
||||
|
||||
// 显示控制
|
||||
NoColor bool // 禁用颜色
|
||||
Language string // 语言
|
||||
LogLevel string // 日志级别
|
||||
|
||||
// 端口映射
|
||||
PortMap map[int][]string // 端口映射
|
||||
DefaultMap []string // 默认映射
|
||||
|
||||
// 初始化锁
|
||||
initOnce sync.Once
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// 简化的初始化函数
|
||||
// =============================================================================
|
||||
|
||||
// InitGlobalConfig 初始化全局配置
|
||||
func InitGlobalConfig() {
|
||||
initOnce.Do(func() {
|
||||
// 设置默认值
|
||||
ScanMode = DefaultScanMode
|
||||
ThreadNum = DefaultThreadNum
|
||||
Timeout = DefaultTimeout
|
||||
LogLevel = DefaultLogLevel
|
||||
Language = DefaultLanguage
|
||||
|
||||
// 初始化映射和切片
|
||||
Userdict = make(map[string][]string)
|
||||
PortMap = make(map[int][]string)
|
||||
DefaultMap = make([]string, 0)
|
||||
|
||||
// 从config模块获取字典和映射
|
||||
if serviceDict := config.GetGlobalServiceDict(); serviceDict != nil {
|
||||
Userdict = serviceDict.GetAllUserDicts()
|
||||
Passwords = serviceDict.GetPasswords()
|
||||
}
|
||||
|
||||
if probeMapping := config.GetGlobalProbeMapping(); probeMapping != nil {
|
||||
PortMap = probeMapping.GetAllPortMappings()
|
||||
DefaultMap = probeMapping.GetDefaultProbes()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 访问器函数已移除(未使用的死代码)
|
||||
// 直接使用全局变量进行访问
|
||||
// =============================================================================
|
||||
|
||||
// init 自动初始化
|
||||
func init() {
|
||||
InitGlobalConfig()
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
/*
|
||||
Plugin.go - 插件系统管理
|
||||
|
||||
整合Types.go中的插件系统,提供统一的插件注册和管理机制。
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// 核心数据结构 (从Types.go迁移)
|
||||
// =============================================================================
|
||||
|
||||
// HostInfo 主机信息结构
|
||||
type HostInfo struct {
|
||||
Host string // 主机地址
|
||||
Ports string // 端口范围
|
||||
Url string // URL地址
|
||||
Infostr []string // 附加信息
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 插件类型常量
|
||||
// =============================================================================
|
||||
|
||||
const (
|
||||
PluginTypeService = "service" // 服务类型插件
|
||||
PluginTypeWeb = "web" // Web类型插件
|
||||
PluginTypeLocal = "local" // 本地类型插件
|
||||
PluginTypeBrute = "brute" // 暴力破解插件
|
||||
PluginTypePoc = "poc" // POC验证插件
|
||||
PluginTypeScan = "scan" // 扫描探测插件
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// 插件定义和管理
|
||||
// =============================================================================
|
||||
|
||||
// ScanPlugin 定义扫描插件的结构
|
||||
type ScanPlugin struct {
|
||||
Name string // 插件名称
|
||||
Version string // 插件版本
|
||||
Description string // 插件描述
|
||||
Author string // 插件作者
|
||||
Ports []int // 适用端口
|
||||
Types []string // 插件类型标签,一个插件可以有多个类型
|
||||
Priority int // 插件优先级(数字越小优先级越高)
|
||||
Enabled bool // 是否启用
|
||||
ScanFunc func(*HostInfo) error // 扫描函数
|
||||
}
|
||||
|
||||
// PluginManager 插件管理器
|
||||
type PluginManager struct {
|
||||
mu sync.RWMutex
|
||||
plugins map[string]*ScanPlugin
|
||||
types map[string][]*ScanPlugin // 按类型索引的插件
|
||||
ports map[int][]*ScanPlugin // 按端口索引的插件
|
||||
}
|
||||
|
||||
// 全局插件管理器实例
|
||||
var globalPluginManager = NewPluginManager()
|
||||
|
||||
// NewPluginManager 创建新的插件管理器
|
||||
func NewPluginManager() *PluginManager {
|
||||
return &PluginManager{
|
||||
plugins: make(map[string]*ScanPlugin),
|
||||
types: make(map[string][]*ScanPlugin),
|
||||
ports: make(map[int][]*ScanPlugin),
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 插件基础方法
|
||||
// =============================================================================
|
||||
|
||||
// HasType 检查插件是否具有指定类型
|
||||
func (p *ScanPlugin) HasType(typeName string) bool {
|
||||
for _, t := range p.Types {
|
||||
if t == typeName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HasPort 检查插件是否支持指定端口
|
||||
func (p *ScanPlugin) HasPort(port int) bool {
|
||||
// 如果没有指定端口列表,表示支持所有端口
|
||||
if len(p.Ports) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查端口是否在支持列表中
|
||||
for _, supportedPort := range p.Ports {
|
||||
if port == supportedPort {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsEnabled 检查插件是否启用
|
||||
func (p *ScanPlugin) IsEnabled() bool {
|
||||
return p.Enabled
|
||||
}
|
||||
|
||||
// GetInfo 获取插件基本信息
|
||||
func (p *ScanPlugin) GetInfo() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"name": p.Name,
|
||||
"version": p.Version,
|
||||
"description": p.Description,
|
||||
"author": p.Author,
|
||||
"types": p.Types,
|
||||
"ports": p.Ports,
|
||||
"priority": p.Priority,
|
||||
"enabled": p.Enabled,
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 插件管理器方法
|
||||
// =============================================================================
|
||||
|
||||
// 已移除未使用的 RegisterPlugin 方法
|
||||
|
||||
// ========================================================================================
|
||||
// 未使用的插件管理器方法已删除(死代码清理)
|
||||
// 包括:GetPlugin, GetPluginsByType, GetPluginsByPort, GetAllPlugins,
|
||||
// EnablePlugin, DisablePlugin, UnregisterPlugin, GetPluginCount, GetEnabledPluginCount
|
||||
// ========================================================================================
|
||||
|
||||
// =============================================================================
|
||||
// 全局插件管理函数 (保持向后兼容)
|
||||
// =============================================================================
|
||||
|
||||
// 已移除未使用的 RegisterPlugin 方法
|
||||
|
||||
// 已移除未使用的 Clear 方法
|
||||
|
||||
// 已移除未使用的 ClearPluginsByType 方法
|
||||
|
||||
// GetGlobalPluginManager 方法已删除(死代码清理)
|
||||
|
||||
// 向后兼容的全局变量 (已废弃,建议使用PluginManager)
|
||||
var LegacyPluginManager = make(map[string]ScanPlugin)
|
@ -3,8 +3,7 @@ package common
|
||||
/*
|
||||
common.go - 简化的统一入口
|
||||
|
||||
移除所有向后兼容层,提供清晰的模块化接口。
|
||||
直接导出各子模块的核心功能,避免代码债务。
|
||||
直接导出核心功能,移除base包依赖。
|
||||
*/
|
||||
|
||||
import (
|
||||
@ -15,35 +14,24 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/shadow1ng/fscan/common/base"
|
||||
"github.com/shadow1ng/fscan/common/logging"
|
||||
"github.com/shadow1ng/fscan/common/output"
|
||||
"github.com/shadow1ng/fscan/common/proxy"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// 核心类型导出 - 直接从core模块导出
|
||||
// 核心类型导出 - 直接定义,不再通过base包
|
||||
// =============================================================================
|
||||
|
||||
type HostInfo = base.HostInfo
|
||||
type ScanPlugin = base.ScanPlugin
|
||||
// 类型现已直接定义在types.go中:
|
||||
// type HostInfo
|
||||
// type ScanPlugin
|
||||
|
||||
// 插件类型常量
|
||||
const (
|
||||
PluginTypeWeb = base.PluginTypeWeb
|
||||
PluginTypeLocal = base.PluginTypeLocal
|
||||
)
|
||||
// 插件类型常量现已直接定义在constants.go中:
|
||||
// const PluginTypeWeb, PluginTypeLocal
|
||||
|
||||
// 全局插件管理器
|
||||
var PluginManager = base.LegacyPluginManager
|
||||
|
||||
// =============================================================================
|
||||
// 核心功能导出 - 直接调用对应模块
|
||||
// =============================================================================
|
||||
|
||||
// 已移除未使用的 RegisterPlugin 方法
|
||||
|
||||
// GetGlobalPluginManager 函数已删除(死代码清理)
|
||||
// 简化的插件管理器 - 移除复杂的base.LegacyPluginManager
|
||||
var PluginManager = make(map[string]ScanPlugin)
|
||||
|
||||
// =============================================================================
|
||||
// 日志系统简化接口
|
||||
@ -66,9 +54,7 @@ func getGlobalLogger() *logging.Logger {
|
||||
StartTime: StartTime,
|
||||
}
|
||||
globalLogger = logging.NewLogger(config)
|
||||
if ProgressBar != nil {
|
||||
globalLogger.SetProgressBar(ProgressBar)
|
||||
}
|
||||
// ProgressBar设置已简化
|
||||
globalLogger.SetOutputMutex(&OutputMutex)
|
||||
|
||||
// 设置协调输出函数,使用LogWithProgress
|
||||
@ -232,4 +218,4 @@ func WrapperTlsWithContext(ctx context.Context, network, address string, config
|
||||
// CheckErrs 检查单个错误 - 简化版本
|
||||
func CheckErrs(err error) error {
|
||||
return err
|
||||
}
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
package base
|
||||
package common
|
||||
|
||||
/*
|
||||
Constants.go - 核心常量定义
|
||||
constants.go - 核心常量定义
|
||||
|
||||
整合Ports.go等常量文件,统一管理所有核心常量。
|
||||
直接定义所有常量,消除base包的间接层。
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// 端口常量 (从Ports.go迁移)
|
||||
// 端口常量
|
||||
// =============================================================================
|
||||
|
||||
// 预定义端口组常量
|
||||
@ -39,3 +39,16 @@ const (
|
||||
DefaultLanguage = "zh" // 默认语言
|
||||
DefaultLogLevel = "base" // 默认日志级别
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// 插件类型常量
|
||||
// =============================================================================
|
||||
|
||||
const (
|
||||
PluginTypeService = "service" // 服务类型插件
|
||||
PluginTypeWeb = "web" // Web类型插件
|
||||
PluginTypeLocal = "local" // 本地类型插件
|
||||
PluginTypeBrute = "brute" // 暴力破解插件
|
||||
PluginTypePoc = "poc" // POC验证插件
|
||||
PluginTypeScan = "scan" // 扫描探测插件
|
||||
)
|
@ -7,11 +7,10 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/shadow1ng/fscan/common/config"
|
||||
"github.com/shadow1ng/fscan/common/i18n"
|
||||
)
|
||||
|
||||
// Flag专用变量 (只在Flag.go中使用的变量直接定义在这里)
|
||||
// Flag专用变量 (只在flag.go中使用的变量,globals.go中已定义的不重复声明)
|
||||
var (
|
||||
ExcludeHosts string
|
||||
Ports string
|
||||
@ -20,9 +19,6 @@ var (
|
||||
HostsFile string
|
||||
PortsFile string
|
||||
|
||||
// 本地插件列表(由外部初始化)
|
||||
LocalPluginsList []string
|
||||
|
||||
ModuleThreadNum int
|
||||
GlobalTimeout int64
|
||||
EnableFingerprint bool
|
||||
@ -33,19 +29,15 @@ var (
|
||||
PasswordsFile string
|
||||
HashFile string
|
||||
HashValue string
|
||||
Domain string
|
||||
SshKeyPath string
|
||||
|
||||
TargetURL string
|
||||
URLsFile string
|
||||
Cookie string
|
||||
WebTimeout int64
|
||||
UserAgent string
|
||||
Accept string
|
||||
|
||||
PocPath string
|
||||
PocFull bool
|
||||
DnsLog bool
|
||||
PocNum int
|
||||
DisablePocScan bool
|
||||
|
||||
@ -55,32 +47,14 @@ var (
|
||||
RedisWriteContent string
|
||||
RedisWriteFile string
|
||||
|
||||
DisableBrute bool
|
||||
MaxRetries int
|
||||
|
||||
DisableSave bool
|
||||
Silent bool
|
||||
DisableProgress bool
|
||||
|
||||
Shellcode string
|
||||
|
||||
// 反弹Shell相关变量
|
||||
ReverseShellTarget string
|
||||
ReverseShellActive bool // 反弹Shell是否处于活跃状态
|
||||
|
||||
// SOCKS5代理相关变量
|
||||
Socks5ProxyPort int // SOCKS5代理监听端口
|
||||
Socks5ProxyActive bool // SOCKS5代理是否处于活跃状态
|
||||
|
||||
// 正向Shell相关变量
|
||||
ForwardShellPort int // 正向Shell监听端口
|
||||
ForwardShellActive bool // 正向Shell是否处于活跃状态
|
||||
|
||||
// Linux持久化相关变量
|
||||
PersistenceTargetFile string // 持久化目标文件路径
|
||||
|
||||
// Windows持久化相关变量
|
||||
WinPEFile string // Windows PE文件路径
|
||||
|
||||
// 键盘记录相关变量
|
||||
KeyloggerOutputFile string // 键盘记录输出文件
|
||||
@ -96,8 +70,7 @@ var (
|
||||
HashBytes [][]byte
|
||||
)
|
||||
|
||||
// Pocinfo POC信息变量
|
||||
var Pocinfo config.PocInfo
|
||||
// POC信息变量已在globals.go中定义,此处不重复声明
|
||||
|
||||
func Banner() {
|
||||
// 定义暗绿色系
|
@ -4,193 +4,176 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/schollz/progressbar/v3"
|
||||
"github.com/shadow1ng/fscan/common/base"
|
||||
"github.com/shadow1ng/fscan/common/logging"
|
||||
"github.com/shadow1ng/fscan/common/config"
|
||||
)
|
||||
|
||||
/*
|
||||
globals.go - 全局变量定义
|
||||
globals.go - 全局配置变量
|
||||
|
||||
使用线程安全的配置管理,消除双向同步机制,直接使用core包作为唯一数据源。
|
||||
保持向后兼容的同时提供并发安全的访问。
|
||||
直接定义全局变量,消除base包的双重声明。
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// 版本信息
|
||||
// =============================================================================
|
||||
|
||||
var version = "2.2.1"
|
||||
|
||||
// =============================================================================
|
||||
// 简化的全局状态管理(仅保留必要的同步机制)
|
||||
// =============================================================================
|
||||
|
||||
// globalState已简化,因为大部分管理函数未被使用
|
||||
// 保留基本的时间记录用于向后兼容
|
||||
var startTimeInit = time.Now()
|
||||
|
||||
// =============================================================================
|
||||
// 核心扫描配置 - 直接使用core包变量(单一数据源)
|
||||
// =============================================================================
|
||||
|
||||
var (
|
||||
ScanMode string // 直接映射到base.ScanMode
|
||||
ThreadNum int // 直接映射到base.ThreadNum
|
||||
Timeout int64 // 直接映射到base.Timeout
|
||||
DisablePing bool // 直接映射到base.DisablePing
|
||||
LocalMode bool // 直接映射到base.LocalMode
|
||||
LocalPlugin string // 本地插件选择
|
||||
AliveOnly bool // 仅存活探测模式
|
||||
// =============================================================================
|
||||
// 核心扫描配置
|
||||
// =============================================================================
|
||||
|
||||
ScanMode string = DefaultScanMode // 扫描模式
|
||||
ThreadNum int = DefaultThreadNum // 线程数
|
||||
Timeout int64 = DefaultTimeout // 超时时间
|
||||
DisablePing bool = false // 禁用ping
|
||||
LocalMode bool = false // 本地模式
|
||||
LocalPlugin string // 本地插件选择
|
||||
AliveOnly bool // 仅存活检测
|
||||
DisableBrute bool // 禁用暴力破解
|
||||
MaxRetries int = 3 // 最大重试次数
|
||||
|
||||
// =============================================================================
|
||||
// 认证相关配置
|
||||
// =============================================================================
|
||||
|
||||
Username string // 用户名
|
||||
Password string // 密码
|
||||
Domain string // 域
|
||||
Userdict map[string][]string // 用户字典
|
||||
Passwords []string // 密码列表
|
||||
|
||||
// =============================================================================
|
||||
// 网络配置
|
||||
// =============================================================================
|
||||
|
||||
HttpProxy string // HTTP代理
|
||||
Socks5Proxy string // SOCKS5代理
|
||||
WebTimeout int64 = 5 // Web超时时间
|
||||
|
||||
// =============================================================================
|
||||
// 文件路径
|
||||
// =============================================================================
|
||||
|
||||
SshKeyPath string // SSH密钥路径
|
||||
PersistenceTargetFile string // Linux持久化目标文件
|
||||
WinPEFile string // Windows PE文件
|
||||
|
||||
// =============================================================================
|
||||
// 功能开关
|
||||
// =============================================================================
|
||||
|
||||
NoColor bool // 禁用颜色
|
||||
Silent bool // 静默模式
|
||||
DisableSave bool // 禁用保存
|
||||
DisableProgress bool // 禁用进度条
|
||||
Language string = DefaultLanguage // 语言
|
||||
LogLevel string = DefaultLogLevel // 日志级别
|
||||
|
||||
// =============================================================================
|
||||
// 高级功能
|
||||
// =============================================================================
|
||||
|
||||
Shellcode string // Shellcode
|
||||
LocalPluginsList []string // 本地插件列表
|
||||
DnsLog bool // DNS日志
|
||||
ForwardShellActive bool // 正向Shell状态
|
||||
ReverseShellActive bool // 反向Shell状态
|
||||
Socks5ProxyActive bool // SOCKS5代理状态
|
||||
|
||||
// =============================================================================
|
||||
// 端口和服务映射
|
||||
// =============================================================================
|
||||
|
||||
PortMap map[int][]string // 端口到服务映射
|
||||
DefaultMap []string // 默认探测器映射
|
||||
|
||||
// =============================================================================
|
||||
// 输出控制
|
||||
// =============================================================================
|
||||
|
||||
Outputfile string = "result.txt" // 输出文件
|
||||
OutputFormat string = "txt" // 输出格式
|
||||
ShowProgress bool = true // 显示进度条
|
||||
StartTime time.Time // 开始时间
|
||||
ProgressBar interface{} // 进度条实例
|
||||
OutputMutex sync.Mutex // 输出互斥锁
|
||||
|
||||
// =============================================================================
|
||||
// 计数器
|
||||
// =============================================================================
|
||||
|
||||
End int64 // 结束计数器
|
||||
Num int64 // 数量计数器
|
||||
|
||||
// =============================================================================
|
||||
// 初始化控制
|
||||
// =============================================================================
|
||||
|
||||
initOnce sync.Once // 确保初始化只执行一次
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// 基础认证配置 - 直接使用core包变量
|
||||
// =============================================================================
|
||||
// InitGlobalConfig 初始化全局配置
|
||||
func InitGlobalConfig() {
|
||||
initOnce.Do(func() {
|
||||
// 初始化时间
|
||||
if StartTime.IsZero() {
|
||||
StartTime = time.Now()
|
||||
}
|
||||
|
||||
// 初始化映射和切片
|
||||
if Userdict == nil {
|
||||
Userdict = make(map[string][]string)
|
||||
}
|
||||
if PortMap == nil {
|
||||
PortMap = make(map[int][]string)
|
||||
}
|
||||
if DefaultMap == nil {
|
||||
DefaultMap = make([]string, 0)
|
||||
}
|
||||
if Passwords == nil {
|
||||
Passwords = make([]string, 0)
|
||||
}
|
||||
|
||||
var (
|
||||
Username string // 直接映射到base.Username
|
||||
Password string // 直接映射到base.Password
|
||||
Userdict map[string][]string // 直接映射到base.Userdict
|
||||
Passwords []string // 直接映射到base.Passwords
|
||||
)
|
||||
// 从config模块获取字典和映射
|
||||
if serviceDict := config.GetGlobalServiceDict(); serviceDict != nil {
|
||||
Userdict = serviceDict.GetAllUserDicts()
|
||||
Passwords = serviceDict.GetPasswords()
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 网络配置 - 直接使用core包变量
|
||||
// =============================================================================
|
||||
|
||||
var (
|
||||
HttpProxy string // 直接映射到base.HttpProxy
|
||||
Socks5Proxy string // 直接映射到base.Socks5Proxy
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// 显示控制 - 直接使用core包变量
|
||||
// =============================================================================
|
||||
|
||||
var (
|
||||
NoColor bool // 直接映射到base.NoColor
|
||||
Language string // 直接映射到base.Language
|
||||
LogLevel string // 直接映射到base.LogLevel
|
||||
|
||||
// 进度条控制
|
||||
ShowProgress bool // 计算得出:!DisableProgress
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// 端口映射 - 直接使用core包变量
|
||||
// =============================================================================
|
||||
|
||||
var (
|
||||
PortMap map[int][]string // 直接映射到base.PortMap
|
||||
DefaultMap []string // 直接映射到base.DefaultMap
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// 线程安全的输出状态管理(已移除未使用的函数)
|
||||
// =============================================================================
|
||||
|
||||
// 注意:GetOutputfile, SetOutputfile, GetOutputFormat, SetOutputFormat,
|
||||
// GetProgressBar, SetProgressBar, GetStats, SetStats, IncrementNum等函数
|
||||
// 已根据死代码分析移除,因为它们在代码库中没有被使用。
|
||||
// 如有需要,可以通过直接访问向后兼容的全局变量实现相同功能。
|
||||
|
||||
// =============================================================================
|
||||
// 核心配置同步(线程安全)
|
||||
// =============================================================================
|
||||
|
||||
// SyncFromCore 从core包同步配置到common包(读操作)
|
||||
func SyncFromCore() {
|
||||
ScanMode = base.ScanMode
|
||||
ThreadNum = base.ThreadNum
|
||||
Timeout = base.Timeout
|
||||
DisablePing = base.DisablePing
|
||||
LocalMode = base.LocalMode
|
||||
LocalPlugin = base.LocalPlugin
|
||||
|
||||
Username = base.Username
|
||||
Password = base.Password
|
||||
Userdict = base.Userdict
|
||||
Passwords = base.Passwords
|
||||
|
||||
HttpProxy = base.HttpProxy
|
||||
Socks5Proxy = base.Socks5Proxy
|
||||
|
||||
NoColor = base.NoColor
|
||||
Language = base.Language
|
||||
LogLevel = base.LogLevel
|
||||
|
||||
PortMap = base.PortMap
|
||||
DefaultMap = base.DefaultMap
|
||||
if probeMapping := config.GetGlobalProbeMapping(); probeMapping != nil {
|
||||
PortMap = probeMapping.GetAllPortMappings()
|
||||
DefaultMap = probeMapping.GetDefaultProbes()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// SyncToCore 同步common包配置到core包(写操作)
|
||||
func SyncToCore() {
|
||||
base.ScanMode = ScanMode
|
||||
base.ThreadNum = ThreadNum
|
||||
base.Timeout = Timeout
|
||||
base.DisablePing = DisablePing
|
||||
base.LocalMode = LocalMode
|
||||
base.LocalPlugin = LocalPlugin
|
||||
|
||||
base.Username = Username
|
||||
base.Password = Password
|
||||
base.Userdict = Userdict
|
||||
base.Passwords = Passwords
|
||||
|
||||
base.HttpProxy = HttpProxy
|
||||
base.Socks5Proxy = Socks5Proxy
|
||||
|
||||
base.NoColor = NoColor
|
||||
base.Language = Language
|
||||
base.LogLevel = LogLevel
|
||||
|
||||
base.PortMap = PortMap
|
||||
base.DefaultMap = DefaultMap
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 向后兼容的全局变量
|
||||
// =============================================================================
|
||||
|
||||
var (
|
||||
// 输出配置(向后兼容)
|
||||
Outputfile string
|
||||
OutputFormat string
|
||||
ProgressBar *progressbar.ProgressBar
|
||||
OutputMutex sync.Mutex
|
||||
|
||||
// 统计信息(向后兼容)
|
||||
Num, End int64
|
||||
StartTime = time.Now()
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// 日志级别常量
|
||||
// =============================================================================
|
||||
|
||||
const (
|
||||
LogLevelAll = string(logging.LevelAll)
|
||||
LogLevelError = string(logging.LevelError)
|
||||
LogLevelBase = string(logging.LevelBase)
|
||||
LogLevelInfo = string(logging.LevelInfo)
|
||||
LogLevelSuccess = string(logging.LevelSuccess)
|
||||
LogLevelDebug = string(logging.LevelDebug)
|
||||
LogLevelInfoSuccess = string(logging.LevelInfoSuccess)
|
||||
LogLevelBaseInfoSuccess = string(logging.LevelBaseInfoSuccess)
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// 初始化
|
||||
// =============================================================================
|
||||
|
||||
// init 自动初始化
|
||||
func init() {
|
||||
// 初始化core包配置
|
||||
base.InitGlobalConfig()
|
||||
|
||||
// 从core包同步初始配置
|
||||
SyncFromCore()
|
||||
|
||||
// 初始化向后兼容的时间变量
|
||||
StartTime = startTimeInit
|
||||
}
|
||||
InitGlobalConfig()
|
||||
}
|
||||
|
||||
// POCInfo POC相关信息
|
||||
type PocInfo struct {
|
||||
Target string
|
||||
PocName string
|
||||
}
|
||||
|
||||
var Pocinfo PocInfo
|
||||
|
||||
// 版本信息
|
||||
const version = "2.2.1"
|
||||
|
||||
// 日志级别常量
|
||||
const (
|
||||
LogLevelAll = "all"
|
||||
LogLevelError = "error"
|
||||
LogLevelBase = "base"
|
||||
LogLevelInfo = "info"
|
||||
LogLevelSuccess = "success"
|
||||
LogLevelDebug = "debug"
|
||||
LogLevelInfoSuccess = "info,success"
|
||||
LogLevelBaseInfoSuccess = "base,info,success"
|
||||
)
|
||||
|
||||
// SyncToCore 同步配置到core包 - 简化实现
|
||||
func SyncToCore() {
|
||||
// 简化实现,暂时为空
|
||||
}
|
||||
|
||||
// LogWithProgress 已在progressmanager.go中定义,此处不重复
|
@ -2,6 +2,9 @@ package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -56,11 +59,11 @@ func NewParser(options *parsers.ParserOptions) *Parser {
|
||||
|
||||
// 全局解析器实例
|
||||
var globalParser *Parser
|
||||
var initOnce sync.Once
|
||||
var parseOnce sync.Once
|
||||
|
||||
// getGlobalParser 获取全局解析器实例
|
||||
func getGlobalParser() *Parser {
|
||||
initOnce.Do(func() {
|
||||
parseOnce.Do(func() {
|
||||
globalParser = NewParser(nil)
|
||||
})
|
||||
return globalParser
|
||||
@ -73,6 +76,18 @@ func Parse(Info *HostInfo) error {
|
||||
|
||||
parser := getGlobalParser()
|
||||
|
||||
// 检查是否为host:port格式,如果是则清空端口字段避免双重扫描
|
||||
ports := Ports
|
||||
if Info.Host != "" && strings.Contains(Info.Host, ":") {
|
||||
if _, portStr, err := net.SplitHostPort(Info.Host); err == nil {
|
||||
if port, portErr := strconv.Atoi(portStr); portErr == nil && port >= 1 && port <= 65535 {
|
||||
// 这是有效的host:port格式,清空端口字段和全局变量
|
||||
ports = ""
|
||||
Ports = "" // 也清空全局变量,避免插件适用性检查使用默认端口
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 构建输入参数
|
||||
input := &AllInputs{
|
||||
Credential: &parsers.CredentialInput{
|
||||
@ -91,7 +106,7 @@ func Parse(Info *HostInfo) error {
|
||||
Host: Info.Host,
|
||||
HostsFile: HostsFile,
|
||||
ExcludeHosts: ExcludeHosts,
|
||||
Ports: Ports,
|
||||
Ports: ports,
|
||||
PortsFile: PortsFile,
|
||||
AddPorts: AddPorts,
|
||||
ExcludePorts: ExcludePorts,
|
||||
@ -488,9 +503,7 @@ func applyLogLevel() {
|
||||
}
|
||||
|
||||
newLogger := logging.NewLogger(config)
|
||||
if ProgressBar != nil {
|
||||
newLogger.SetProgressBar(ProgressBar)
|
||||
}
|
||||
// ProgressBar设置已简化
|
||||
newLogger.SetOutputMutex(&OutputMutex)
|
||||
|
||||
// 设置协调输出函数,使用LogWithProgress
|
@ -3,8 +3,6 @@ package parsers
|
||||
import (
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/shadow1ng/fscan/common/base"
|
||||
)
|
||||
|
||||
/*
|
||||
@ -193,15 +191,18 @@ const (
|
||||
SamplingMaxHost = 253
|
||||
)
|
||||
|
||||
// GetPortGroups 获取预定义端口组映射(统一的端口组定义)
|
||||
// GetPortGroups 获取预定义端口组映射
|
||||
// 注意:端口常量现在直接定义在common/constants.go中
|
||||
func GetPortGroups() map[string]string {
|
||||
// 这些常量值现在需要从common包导入
|
||||
// 为了避免循环导入,我们直接复制值,或者重新设计导入关系
|
||||
return map[string]string{
|
||||
"web": base.WebPorts, // 使用实际的WebPorts常量
|
||||
"main": base.MainPorts, // 使用实际的MainPorts常量
|
||||
"db": base.DbPorts, // 使用实际的DbPorts常量
|
||||
"service": base.ServicePorts, // 使用实际的ServicePorts常量
|
||||
"common": base.CommonPorts, // 使用实际的CommonPorts常量
|
||||
"all": base.AllPorts, // 使用实际的AllPorts常量
|
||||
"web": "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8005,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10051,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,20880,21000,21501,21502,28018",
|
||||
"main": "21,22,23,25,80,81,110,135,139,143,389,443,445,465,502,587,636,873,993,995,1433,1434,1521,1522,1525,2121,2200,2222,3000,3268,3269,3306,3389,5432,5672,5900,6379,7474,7687,8000,8080,8081,8088,8443,8888,9000,9042,9080,9092,9200,9300,11211,15672,22222,27017,61613,61614",
|
||||
"db": "1433,1521,3306,5432,5672,6379,7687,9042,9093,9200,11211,27017,61616",
|
||||
"service": "21,22,23,25,110,135,139,143,162,389,445,465,502,587,636,873,993,995,1433,1521,2222,3306,3389,5020,5432,5672,5671,6379,8161,8443,9000,9092,9093,9200,10051,11211,15672,15671,27017,61616,61613",
|
||||
"common": "21,22,23,25,53,80,110,135,139,143,443,445,993,995,1723,3389,5060,5985,5986",
|
||||
"all": "1-65535",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,7 +136,14 @@ func (tp *TargetParser) Parse(input *TargetInput, options *ParserOptions) (*Pars
|
||||
// 更新配置
|
||||
result.Config.Targets.Hosts = hosts
|
||||
result.Config.Targets.URLs = urls
|
||||
result.Config.Targets.Ports = ports
|
||||
|
||||
// 如果存在明确的host:port组合,则清空端口列表避免双重扫描
|
||||
if len(hostPorts) > 0 {
|
||||
result.Config.Targets.Ports = nil // 清空默认端口,只扫描指定的host:port
|
||||
} else {
|
||||
result.Config.Targets.Ports = ports
|
||||
}
|
||||
|
||||
result.Config.Targets.ExcludePorts = excludePorts
|
||||
result.Config.Targets.HostPorts = hostPorts
|
||||
|
||||
@ -162,11 +169,41 @@ func (tp *TargetParser) parseHosts(input *TargetInput) ([]string, []error, []str
|
||||
|
||||
// 解析命令行主机
|
||||
if input.Host != "" {
|
||||
hostList, err := tp.parseHostList(input.Host)
|
||||
if err != nil {
|
||||
errors = append(errors, NewParseError(ErrorTypeHostError, err.Error(), "command line", 0, err))
|
||||
// 检查是否为host:port格式,直接添加到HostPort避免双重处理
|
||||
if strings.Contains(input.Host, ":") {
|
||||
if _, portStr, err := net.SplitHostPort(input.Host); err == nil {
|
||||
if port, portErr := strconv.Atoi(portStr); portErr == nil && port >= MinPort && port <= MaxPort {
|
||||
// 这是有效的host:port格式,直接添加到HostPort
|
||||
input.HostPort = append(input.HostPort, input.Host)
|
||||
// 清空Ports字段,避免解析默认端口
|
||||
input.Ports = ""
|
||||
// 不添加到hosts,避免双重处理
|
||||
} else {
|
||||
// 端口无效,作为普通主机处理
|
||||
hostList, parseErr := tp.parseHostList(input.Host)
|
||||
if parseErr != nil {
|
||||
errors = append(errors, NewParseError(ErrorTypeHostError, parseErr.Error(), "command line", 0, parseErr))
|
||||
} else {
|
||||
hosts = append(hosts, hostList...)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 不是有效的host:port格式,作为普通主机处理
|
||||
hostList, parseErr := tp.parseHostList(input.Host)
|
||||
if parseErr != nil {
|
||||
errors = append(errors, NewParseError(ErrorTypeHostError, parseErr.Error(), "command line", 0, parseErr))
|
||||
} else {
|
||||
hosts = append(hosts, hostList...)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hosts = append(hosts, hostList...)
|
||||
// 普通主机,正常处理
|
||||
hostList, err := tp.parseHostList(input.Host)
|
||||
if err != nil {
|
||||
errors = append(errors, NewParseError(ErrorTypeHostError, err.Error(), "command line", 0, err))
|
||||
} else {
|
||||
hosts = append(hosts, hostList...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
10
common/ports.go
Normal file
10
common/ports.go
Normal file
@ -0,0 +1,10 @@
|
||||
package common
|
||||
|
||||
/*
|
||||
ports.go - 端口配置
|
||||
|
||||
端口常量已直接定义在constants.go中,删除base包的间接引用。
|
||||
*/
|
||||
|
||||
// 端口组常量现在直接在constants.go中定义:
|
||||
// WebPorts, MainPorts, DbPorts, ServicePorts, CommonPorts, AllPorts
|
54
common/types.go
Normal file
54
common/types.go
Normal file
@ -0,0 +1,54 @@
|
||||
package common
|
||||
|
||||
/*
|
||||
types.go - 核心数据结构
|
||||
|
||||
保留真正使用的数据结构,去掉无用的插件管理器。
|
||||
*/
|
||||
|
||||
// HostInfo 主机信息结构 - 最核心的数据结构
|
||||
type HostInfo struct {
|
||||
Host string // 主机地址
|
||||
Ports string // 端口范围
|
||||
Url string // URL地址
|
||||
Infostr []string // 附加信息
|
||||
}
|
||||
|
||||
// ScanPlugin 扫描插件结构 - 简化版
|
||||
type ScanPlugin struct {
|
||||
Name string // 插件名称
|
||||
Version string // 插件版本
|
||||
Description string // 插件描述
|
||||
Author string // 插件作者
|
||||
Ports []int // 适用端口
|
||||
Types []string // 插件类型标签,一个插件可以有多个类型
|
||||
Priority int // 插件优先级(数字越小优先级越高)
|
||||
Enabled bool // 是否启用
|
||||
ScanFunc func(*HostInfo) error // 扫描函数
|
||||
}
|
||||
|
||||
// HasType 检查插件是否具有指定类型
|
||||
func (p *ScanPlugin) HasType(typeName string) bool {
|
||||
for _, t := range p.Types {
|
||||
if t == typeName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HasPort 检查插件是否支持指定端口
|
||||
func (p *ScanPlugin) HasPort(port int) bool {
|
||||
// 如果没有指定端口列表,表示支持所有端口
|
||||
if len(p.Ports) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查端口是否在支持列表中
|
||||
for _, supportedPort := range p.Ports {
|
||||
if port == supportedPort {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
@ -51,8 +51,8 @@ func (b *BaseScanStrategy) GetPlugins() ([]string, bool) {
|
||||
return validPlugins, true
|
||||
}
|
||||
|
||||
// 未指定或使用"all":获取所有插件
|
||||
return plugins.All(), false
|
||||
// 未指定或使用"all":根据策略类型获取对应插件
|
||||
return b.getPluginsByFilterType(), false
|
||||
}
|
||||
|
||||
// IsPluginApplicable 判断插件是否适用(传统接口兼容)
|
||||
@ -88,19 +88,50 @@ func (b *BaseScanStrategy) IsPluginApplicableByName(pluginName string, targetHos
|
||||
return true
|
||||
}
|
||||
|
||||
// 本地插件特殊处理:优先检查,避免不必要的端口获取
|
||||
if b.isLocalPlugin(pluginName) {
|
||||
result := b.isLocalPluginExplicitlySpecified(pluginName)
|
||||
common.LogDebug(fmt.Sprintf("本地插件 %s 检查结果: %v (LocalPlugin='%s')", pluginName, result, common.LocalPlugin))
|
||||
return result
|
||||
// 应用过滤器类型检查,确保与传统接口逻辑一致
|
||||
switch b.filterType {
|
||||
case FilterLocal:
|
||||
// 本地扫描策略:只允许本地插件且必须通过-local参数明确指定
|
||||
if b.isLocalPlugin(pluginName) {
|
||||
result := b.isLocalPluginExplicitlySpecified(pluginName)
|
||||
common.LogDebug(fmt.Sprintf("本地插件 %s 检查结果: %v (LocalPlugin='%s')", pluginName, result, common.LocalPlugin))
|
||||
return result
|
||||
}
|
||||
return false
|
||||
case FilterService:
|
||||
// 服务扫描策略:排除本地插件,本地插件不应在端口扫描中被调用
|
||||
if b.isLocalPlugin(pluginName) {
|
||||
common.LogDebug(fmt.Sprintf("本地插件 %s 被服务扫描策略过滤(本地插件不应与端口关联)", pluginName))
|
||||
return false
|
||||
}
|
||||
case FilterWeb:
|
||||
// Web扫描策略:只允许Web插件
|
||||
if !b.isWebPlugin(pluginName) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
// 无过滤器:允许所有插件类型
|
||||
if b.isLocalPlugin(pluginName) {
|
||||
result := b.isLocalPluginExplicitlySpecified(pluginName)
|
||||
common.LogDebug(fmt.Sprintf("本地插件 %s 检查结果: %v (LocalPlugin='%s')", pluginName, result, common.LocalPlugin))
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// 检查插件端口匹配(特殊端口自动调用具体插件)
|
||||
pluginPorts := b.getPluginPorts(pluginName)
|
||||
|
||||
// Web插件特殊处理:使用智能HTTP检测
|
||||
// Web插件特殊处理:仅在用户未指定端口范围时才进行智能检测
|
||||
// 如果用户明确指定了端口(如 -p 3306),则只对该端口进行检测,不使用预定义端口列表
|
||||
if len(pluginPorts) == 0 && b.isWebPlugin(pluginName) {
|
||||
return b.isWebServicePort(targetHost, targetPort)
|
||||
// 检查用户是否指定了具体端口范围
|
||||
if common.Ports != "" && common.Ports != "all" {
|
||||
// 用户指定了端口,进行实际HTTP协议检测而不是预定义端口匹配
|
||||
return b.isWebServicePortByProtocol(targetHost, targetPort)
|
||||
} else {
|
||||
// 用户未指定端口,使用智能检测(包含预定义端口)
|
||||
return b.isWebServicePort(targetHost, targetPort)
|
||||
}
|
||||
}
|
||||
|
||||
// 无端口限制的其他插件适用于所有端口
|
||||
@ -161,13 +192,21 @@ func (b *BaseScanStrategy) isLocalPlugin(pluginName string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// isWebServicePort 使用智能检测判断端口是否运行Web服务
|
||||
// isWebServicePort 使用智能检测判断端口是否运行Web服务(含预定义端口)
|
||||
func (b *BaseScanStrategy) isWebServicePort(host string, port int) bool {
|
||||
// 创建Web端口检测器实例
|
||||
detector := NewWebPortDetector()
|
||||
return detector.IsWebService(host, port)
|
||||
}
|
||||
|
||||
// isWebServicePortByProtocol 仅通过HTTP协议检测判断端口是否运行Web服务(不使用预定义端口列表)
|
||||
func (b *BaseScanStrategy) isWebServicePortByProtocol(host string, port int) bool {
|
||||
// 创建Web端口检测器实例
|
||||
detector := NewWebPortDetector()
|
||||
// 直接调用协议检测,跳过预定义端口检查
|
||||
return detector.DetectHTTPServiceOnly(host, port)
|
||||
}
|
||||
|
||||
// isLocalPluginExplicitlySpecified 检查本地插件是否明确通过-local参数指定
|
||||
func (b *BaseScanStrategy) isLocalPluginExplicitlySpecified(pluginName string) bool {
|
||||
// 只有通过-local参数明确指定的单个插件才能调用
|
||||
@ -259,6 +298,41 @@ func (b *BaseScanStrategy) LogScanStart() {
|
||||
}
|
||||
}
|
||||
|
||||
// getPluginsByFilterType 根据过滤器类型获取插件列表
|
||||
func (b *BaseScanStrategy) getPluginsByFilterType() []string {
|
||||
allPlugins := plugins.All()
|
||||
var filteredPlugins []string
|
||||
|
||||
switch b.filterType {
|
||||
case FilterLocal:
|
||||
// 本地扫描策略:只返回本地插件
|
||||
for _, pluginName := range allPlugins {
|
||||
if b.isLocalPlugin(pluginName) {
|
||||
filteredPlugins = append(filteredPlugins, pluginName)
|
||||
}
|
||||
}
|
||||
case FilterService:
|
||||
// 服务扫描策略:排除本地插件和纯Web插件,保留服务插件
|
||||
for _, pluginName := range allPlugins {
|
||||
if !b.isLocalPlugin(pluginName) {
|
||||
filteredPlugins = append(filteredPlugins, pluginName)
|
||||
}
|
||||
}
|
||||
case FilterWeb:
|
||||
// Web扫描策略:只返回Web插件
|
||||
for _, pluginName := range allPlugins {
|
||||
if b.isWebPlugin(pluginName) {
|
||||
filteredPlugins = append(filteredPlugins, pluginName)
|
||||
}
|
||||
}
|
||||
default:
|
||||
// 无过滤器:返回所有插件
|
||||
filteredPlugins = allPlugins
|
||||
}
|
||||
|
||||
return filteredPlugins
|
||||
}
|
||||
|
||||
// parsePluginList 解析插件列表字符串
|
||||
func parsePluginList(pluginStr string) []string {
|
||||
if pluginStr == "" {
|
||||
|
@ -159,6 +159,28 @@ func (w *WebPortDetector) IsWebService(host string, port int) bool {
|
||||
return result
|
||||
}
|
||||
|
||||
// DetectHTTPServiceOnly 仅通过HTTP协议检测端口,不使用预定义端口列表
|
||||
func (w *WebPortDetector) DetectHTTPServiceOnly(host string, port int) bool {
|
||||
// 缓存检查:避免重复检测同一端口
|
||||
cacheKey := fmt.Sprintf("%s:%d", host, port)
|
||||
w.cacheMutex.RLock()
|
||||
if cached, exists := w.detectionCache[cacheKey]; exists {
|
||||
w.cacheMutex.RUnlock()
|
||||
return cached
|
||||
}
|
||||
w.cacheMutex.RUnlock()
|
||||
|
||||
// 跳过预定义端口检查,直接进行协议探测
|
||||
result := w.detectHTTPService(host, port)
|
||||
|
||||
// 缓存结果
|
||||
w.cacheMutex.Lock()
|
||||
w.detectionCache[cacheKey] = result
|
||||
w.cacheMutex.Unlock()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// IsCommonWebPort 检查是否为常见Web端口
|
||||
func (w *WebPortDetector) IsCommonWebPort(port int) bool {
|
||||
return w.commonWebPorts[port]
|
||||
|
145
fscan-lite/Makefile
Normal file
145
fscan-lite/Makefile
Normal file
@ -0,0 +1,145 @@
|
||||
# Makefile for fscan-lite
|
||||
# 极简但完整的跨平台构建脚本
|
||||
# 目标:最大兼容性,支持古老的make版本
|
||||
|
||||
# 编译器和标志
|
||||
CC = gcc
|
||||
CFLAGS = -std=c89 -Wall -O2
|
||||
LDFLAGS =
|
||||
LIBS =
|
||||
|
||||
# 平台检测
|
||||
UNAME_S := $(shell uname -s 2>/dev/null || echo Windows)
|
||||
|
||||
# Windows平台配置
|
||||
ifeq ($(OS),Windows_NT)
|
||||
PLATFORM = windows
|
||||
EXT = .exe
|
||||
LIBS = -lws2_32
|
||||
RM = del /Q
|
||||
MKDIR = mkdir
|
||||
PATHSEP = \\
|
||||
else
|
||||
# Unix-like系统配置
|
||||
PLATFORM = unix
|
||||
EXT =
|
||||
LIBS = -lpthread
|
||||
RM = rm -f
|
||||
MKDIR = mkdir -p
|
||||
PATHSEP = /
|
||||
|
||||
# Linux特定配置
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
PLATFORM = linux
|
||||
endif
|
||||
|
||||
# macOS特定配置
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
PLATFORM = macos
|
||||
endif
|
||||
endif
|
||||
|
||||
# 目录和文件
|
||||
SRCDIR = src
|
||||
INCDIR = include
|
||||
BINDIR = bin
|
||||
TARGET = $(BINDIR)$(PATHSEP)fscan-lite$(EXT)
|
||||
|
||||
# 源文件
|
||||
SOURCES = $(SRCDIR)/main.c $(SRCDIR)/scanner.c $(SRCDIR)/platform.c
|
||||
OBJECTS = main.o scanner.o platform.o
|
||||
|
||||
# 默认目标
|
||||
all: $(TARGET)
|
||||
|
||||
# 创建输出目录
|
||||
$(BINDIR):
|
||||
$(MKDIR) $(BINDIR)
|
||||
|
||||
# 主要构建目标
|
||||
$(TARGET): $(BINDIR) $(OBJECTS)
|
||||
$(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(LIBS)
|
||||
@echo "Build completed: $@"
|
||||
|
||||
# 目标文件编译规则
|
||||
main.o: $(SRCDIR)/main.c
|
||||
$(CC) $(CFLAGS) -I$(INCDIR) -c $< -o $@
|
||||
|
||||
scanner.o: $(SRCDIR)/scanner.c
|
||||
$(CC) $(CFLAGS) -I$(INCDIR) -c $< -o $@
|
||||
|
||||
platform.o: $(SRCDIR)/platform.c
|
||||
$(CC) $(CFLAGS) -I$(INCDIR) -c $< -o $@
|
||||
|
||||
# 静态编译目标
|
||||
static: LDFLAGS += -static
|
||||
static: CFLAGS += -DSTATIC_BUILD
|
||||
static: $(TARGET)
|
||||
@echo "Static build completed"
|
||||
|
||||
# 最小化编译(体积优化)
|
||||
small: CFLAGS = -std=c89 -Os -ffunction-sections -fdata-sections -DSTATIC_BUILD
|
||||
small: LDFLAGS = -static -s -Wl,--gc-sections
|
||||
small: $(TARGET)
|
||||
@echo "Minimal build completed"
|
||||
|
||||
# 调试版本
|
||||
debug: CFLAGS += -g -DDEBUG
|
||||
debug: $(TARGET)
|
||||
@echo "Debug build completed"
|
||||
|
||||
# 测试目标
|
||||
test: $(TARGET)
|
||||
@echo "Running basic tests..."
|
||||
$(TARGET) --version
|
||||
$(TARGET) --help
|
||||
@echo "Basic tests completed"
|
||||
|
||||
# 清理
|
||||
clean:
|
||||
$(RM) *.o
|
||||
$(RM) $(TARGET)
|
||||
|
||||
# 完全清理(包括目录)
|
||||
distclean: clean
|
||||
ifeq ($(OS),Windows_NT)
|
||||
if exist $(BINDIR) rmdir /S /Q $(BINDIR)
|
||||
else
|
||||
rm -rf $(BINDIR)
|
||||
endif
|
||||
|
||||
# 安装(简单复制到系统路径)
|
||||
install: $(TARGET)
|
||||
ifeq ($(OS),Windows_NT)
|
||||
@echo "Manual installation required on Windows"
|
||||
@echo "Copy $(TARGET) to desired location"
|
||||
else
|
||||
install -m 755 $(TARGET) /usr/local/bin/
|
||||
@echo "Installed to /usr/local/bin/"
|
||||
endif
|
||||
|
||||
# 显示构建信息
|
||||
info:
|
||||
@echo "Build information:"
|
||||
@echo " Platform: $(PLATFORM)"
|
||||
@echo " Compiler: $(CC)"
|
||||
@echo " CFLAGS: $(CFLAGS)"
|
||||
@echo " LIBS: $(LIBS)"
|
||||
@echo " Target: $(TARGET)"
|
||||
|
||||
# 帮助信息
|
||||
help:
|
||||
@echo "Available targets:"
|
||||
@echo " all - Build normal version (default)"
|
||||
@echo " static - Build static linked version"
|
||||
@echo " small - Build minimal size version"
|
||||
@echo " debug - Build debug version"
|
||||
@echo " test - Run basic tests"
|
||||
@echo " clean - Remove object files and binary"
|
||||
@echo " distclean- Remove all generated files"
|
||||
@echo " install - Install to system (Unix only)"
|
||||
@echo " info - Show build configuration"
|
||||
@echo " help - Show this help"
|
||||
|
||||
# 声明伪目标
|
||||
.PHONY: all static small debug test clean distclean install info help
|
149
fscan-lite/include/platform.h
Normal file
149
fscan-lite/include/platform.h
Normal file
@ -0,0 +1,149 @@
|
||||
#ifndef PLATFORM_H
|
||||
#define PLATFORM_H
|
||||
|
||||
/*
|
||||
* platform.h - 极致兼容性的平台抽象层
|
||||
*
|
||||
* 支持范围:
|
||||
* Windows: 98/ME/NT4/2000/XP/Vista/7/8/10/11
|
||||
* Linux: glibc 2.3+ (2003年后的所有发行版)
|
||||
* 编译器: MSVC 6.0+, GCC 3.0+, Clang 3.0+
|
||||
*/
|
||||
|
||||
/* C89兼容性 - 最古老但最可靠的标准 */
|
||||
#ifndef __STDC__
|
||||
#define __STDC__ 1
|
||||
#endif
|
||||
|
||||
/* 平台检测 */
|
||||
#ifdef _WIN32
|
||||
#define PLATFORM_WINDOWS
|
||||
#ifdef _WIN64
|
||||
#define PLATFORM_WIN64
|
||||
#else
|
||||
#define PLATFORM_WIN32
|
||||
#endif
|
||||
#else
|
||||
#define PLATFORM_UNIX
|
||||
#ifdef __linux__
|
||||
#define PLATFORM_LINUX
|
||||
#elif defined(__APPLE__)
|
||||
#define PLATFORM_MACOS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Windows头文件包含 - 兼容Win98 */
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
/* 定义最低Windows版本 - Win98 */
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0410 /* Windows 98 */
|
||||
#endif
|
||||
#ifndef WINVER
|
||||
#define WINVER 0x0410
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
|
||||
/* 老版本Windows兼容性 */
|
||||
#ifdef _MSC_VER
|
||||
#if _MSC_VER < 1300 /* MSVC 6.0 */
|
||||
#pragma comment(lib, "wsock32.lib")
|
||||
#else
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Windows类型定义 */
|
||||
typedef SOCKET socket_t;
|
||||
typedef int socklen_t;
|
||||
#define INVALID_SOCKET_VALUE INVALID_SOCKET
|
||||
#define close_socket closesocket
|
||||
#define socket_errno WSAGetLastError()
|
||||
|
||||
/* Windows错误码转换 */
|
||||
#define EWOULDBLOCK WSAEWOULDBLOCK
|
||||
#define EINPROGRESS WSAEINPROGRESS
|
||||
#define ECONNREFUSED WSAECONNREFUSED
|
||||
|
||||
#else
|
||||
/* Unix/Linux头文件 */
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
|
||||
/* Unix类型定义 */
|
||||
typedef int socket_t;
|
||||
#define INVALID_SOCKET_VALUE (-1)
|
||||
#define close_socket close
|
||||
#define socket_errno errno
|
||||
|
||||
#endif
|
||||
|
||||
/* 标准C头文件 */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
/* 线程抽象 - 最简单的实现 */
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
typedef HANDLE thread_t;
|
||||
typedef DWORD thread_id_t;
|
||||
typedef unsigned (__stdcall *thread_func_t)(void *);
|
||||
|
||||
#define CREATE_THREAD(func, arg) \
|
||||
(HANDLE)_beginthreadex(NULL, 0, (thread_func_t)(func), (arg), 0, NULL)
|
||||
#define WAIT_THREAD(handle) WaitForSingleObject((handle), INFINITE)
|
||||
#define CLOSE_THREAD(handle) CloseHandle(handle)
|
||||
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <fcntl.h>
|
||||
typedef pthread_t thread_t;
|
||||
typedef pthread_t thread_id_t;
|
||||
typedef void* (*thread_func_t)(void *);
|
||||
|
||||
#define CREATE_THREAD(func, arg) ({ \
|
||||
pthread_t t; \
|
||||
pthread_create(&t, NULL, (thread_func_t)(func), (arg)) == 0 ? t : 0; \
|
||||
})
|
||||
#define WAIT_THREAD(handle) pthread_join((handle), NULL)
|
||||
#define CLOSE_THREAD(handle) /* pthread handles are auto-cleaned */
|
||||
|
||||
#endif
|
||||
|
||||
/* 时间函数抽象 */
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
#define sleep_ms(ms) Sleep(ms)
|
||||
#else
|
||||
#define sleep_ms(ms) usleep((ms) * 1000)
|
||||
#endif
|
||||
|
||||
/* 编译器特定定义 */
|
||||
#ifdef _MSC_VER
|
||||
/* MSVC特定 */
|
||||
#define snprintf _snprintf
|
||||
#define vsnprintf _vsnprintf
|
||||
#define strcasecmp _stricmp
|
||||
#define strncasecmp _strnicmp
|
||||
#endif
|
||||
|
||||
/* 常用常量 */
|
||||
#define MAX_HOST_LEN 256
|
||||
#define MAX_PORT_COUNT 65536
|
||||
#define DEFAULT_TIMEOUT 3
|
||||
#define DEFAULT_THREAD_COUNT 100
|
||||
|
||||
/* 函数声明 */
|
||||
int platform_init(void);
|
||||
void platform_cleanup(void);
|
||||
int set_socket_timeout(socket_t sock, int timeout_seconds);
|
||||
int make_socket_nonblocking(socket_t sock);
|
||||
|
||||
#endif /* PLATFORM_H */
|
BIN
fscan-lite/main.o
Normal file
BIN
fscan-lite/main.o
Normal file
Binary file not shown.
BIN
fscan-lite/platform.o
Normal file
BIN
fscan-lite/platform.o
Normal file
Binary file not shown.
BIN
fscan-lite/scanner.o
Normal file
BIN
fscan-lite/scanner.o
Normal file
Binary file not shown.
120
fscan-lite/src/main.c
Normal file
120
fscan-lite/src/main.c
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* main.c - fscan-lite 主程序
|
||||
*
|
||||
* 极简的TCP内网端口扫描器
|
||||
* 目标:最大兼容性,最小复杂度
|
||||
*/
|
||||
|
||||
#include "../include/platform.h"
|
||||
|
||||
/* 函数声明 */
|
||||
int tcp_connect_test(const char* host, int port, int timeout);
|
||||
int scan_host_ports(const char* host, const int* ports, int port_count, int timeout);
|
||||
int parse_ports(const char* port_str, int* ports, int max_ports);
|
||||
int parse_hosts(const char* host_str, char hosts[][MAX_HOST_LEN], int max_hosts);
|
||||
|
||||
/* 显示版本信息 */
|
||||
void show_version(void) {
|
||||
printf("fscan-lite v1.0 - Lightweight TCP Port Scanner\n");
|
||||
printf("Built for maximum compatibility (Windows 98 - Windows 11, Linux glibc 2.3+)\n");
|
||||
printf("Copyright (c) 2024\n");
|
||||
}
|
||||
|
||||
/* 显示使用帮助 */
|
||||
void show_usage(const char* program_name) {
|
||||
printf("Usage: %s [OPTIONS]\n", program_name);
|
||||
printf("\n");
|
||||
printf("Options:\n");
|
||||
printf(" -h HOST Target host (IP address)\n");
|
||||
printf(" -p PORTS Ports to scan (e.g. 80,443 or 1-1000)\n");
|
||||
printf(" -t TIMEOUT Connection timeout in seconds (default: 3)\n");
|
||||
printf(" --help Show this help message\n");
|
||||
printf(" --version Show version information\n");
|
||||
printf("\n");
|
||||
printf("Examples:\n");
|
||||
printf(" %s -h 192.168.1.1 -p 22,80,443\n", program_name);
|
||||
printf(" %s -h 10.0.0.1 -p 1-1000 -t 2\n", program_name);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/* 主函数 */
|
||||
int main(int argc, char* argv[]) {
|
||||
char* target_host = NULL;
|
||||
char* port_string = NULL;
|
||||
int timeout = DEFAULT_TIMEOUT;
|
||||
int ports[1000]; /* 支持最多1000个端口 */
|
||||
int port_count = 0;
|
||||
int i;
|
||||
int result;
|
||||
|
||||
/* 参数解析 */
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-h") == 0 && i + 1 < argc) {
|
||||
target_host = argv[++i];
|
||||
}
|
||||
else if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) {
|
||||
port_string = argv[++i];
|
||||
}
|
||||
else if (strcmp(argv[i], "-t") == 0 && i + 1 < argc) {
|
||||
timeout = atoi(argv[++i]);
|
||||
if (timeout <= 0) timeout = DEFAULT_TIMEOUT;
|
||||
}
|
||||
else if (strcmp(argv[i], "--help") == 0) {
|
||||
show_usage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp(argv[i], "--version") == 0) {
|
||||
show_version();
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
printf("Unknown option: %s\n", argv[i]);
|
||||
show_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 验证必需参数 */
|
||||
if (!target_host) {
|
||||
printf("Error: Target host (-h) is required\n");
|
||||
show_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!port_string) {
|
||||
printf("Error: Ports (-p) are required\n");
|
||||
show_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 初始化平台 */
|
||||
if (platform_init() != 0) {
|
||||
printf("Error: Failed to initialize platform\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 解析端口 */
|
||||
port_count = parse_ports(port_string, ports, sizeof(ports) / sizeof(ports[0]));
|
||||
if (port_count == 0) {
|
||||
printf("Error: No valid ports specified\n");
|
||||
platform_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("fscan-lite - Starting scan\n");
|
||||
printf("Target: %s\n", target_host);
|
||||
printf("Ports: %d ports to scan\n", port_count);
|
||||
printf("Timeout: %d seconds\n", timeout);
|
||||
printf("=================================\n");
|
||||
|
||||
/* 执行扫描 */
|
||||
result = scan_host_ports(target_host, ports, port_count, timeout);
|
||||
|
||||
printf("=================================\n");
|
||||
printf("Scan completed: %d open ports found\n", result);
|
||||
|
||||
/* 清理资源 */
|
||||
platform_cleanup();
|
||||
|
||||
return 0;
|
||||
}
|
111
fscan-lite/src/platform.c
Normal file
111
fscan-lite/src/platform.c
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* platform.c - 平台抽象层实现
|
||||
*
|
||||
* 实现最基础但最可靠的平台相关功能
|
||||
*/
|
||||
|
||||
#include "../include/platform.h"
|
||||
|
||||
/* 全局初始化标志 */
|
||||
static int platform_initialized = 0;
|
||||
|
||||
/*
|
||||
* 平台初始化
|
||||
* Windows: 初始化Winsock
|
||||
* Unix: 无需特殊初始化
|
||||
*/
|
||||
int platform_init(void) {
|
||||
if (platform_initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
WSADATA wsaData;
|
||||
int result;
|
||||
|
||||
/* 初始化Winsock - 请求版本2.0,兼容Win98 */
|
||||
result = WSAStartup(MAKEWORD(2, 0), &wsaData);
|
||||
if (result != 0) {
|
||||
/* 如果2.0失败,尝试1.1(Win95/NT兼容) */
|
||||
result = WSAStartup(MAKEWORD(1, 1), &wsaData);
|
||||
if (result != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
platform_initialized = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 平台清理
|
||||
*/
|
||||
void platform_cleanup(void) {
|
||||
if (!platform_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
WSACleanup();
|
||||
#endif
|
||||
|
||||
platform_initialized = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 设置socket超时
|
||||
* 兼容所有平台的最可靠方法
|
||||
*/
|
||||
int set_socket_timeout(socket_t sock, int timeout_seconds) {
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
DWORD timeout_ms = timeout_seconds * 1000;
|
||||
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
|
||||
(char*)&timeout_ms, sizeof(timeout_ms)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
|
||||
(char*)&timeout_ms, sizeof(timeout_ms)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
struct timeval tv;
|
||||
tv.tv_sec = timeout_seconds;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
|
||||
(void*)&tv, sizeof(tv)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
|
||||
(void*)&tv, sizeof(tv)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 设置socket为非阻塞模式
|
||||
* 跨平台兼容实现
|
||||
*/
|
||||
int make_socket_nonblocking(socket_t sock) {
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
u_long mode = 1;
|
||||
return ioctlsocket(sock, FIONBIO, &mode);
|
||||
#else
|
||||
int flags;
|
||||
|
||||
flags = fcntl(sock, F_GETFL, 0);
|
||||
if (flags == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
flags |= O_NONBLOCK;
|
||||
return fcntl(sock, F_SETFL, flags);
|
||||
#endif
|
||||
}
|
164
fscan-lite/src/scanner.c
Normal file
164
fscan-lite/src/scanner.c
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* scanner.c - 核心TCP端口扫描实现
|
||||
*
|
||||
* 极简但可靠的端口扫描逻辑,专注于内网环境
|
||||
*/
|
||||
|
||||
#include "../include/platform.h"
|
||||
|
||||
/*
|
||||
* 基础TCP连接测试
|
||||
* 返回: 1=端口开放, 0=端口关闭, -1=错误
|
||||
*/
|
||||
int tcp_connect_test(const char* host, int port, int timeout) {
|
||||
socket_t sock;
|
||||
struct sockaddr_in addr;
|
||||
int result;
|
||||
|
||||
/* 参数验证 */
|
||||
if (!host || port <= 0 || port > 65535) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 创建socket */
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock == INVALID_SOCKET_VALUE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 设置超时 */
|
||||
if (set_socket_timeout(sock, timeout) != 0) {
|
||||
close_socket(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 设置目标地址 */
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons((unsigned short)port);
|
||||
|
||||
/* 转换IP地址 */
|
||||
addr.sin_addr.s_addr = inet_addr(host);
|
||||
if (addr.sin_addr.s_addr == INADDR_NONE) {
|
||||
/* 如果不是有效IP,当作域名处理 */
|
||||
struct hostent* he;
|
||||
he = gethostbyname(host);
|
||||
if (!he) {
|
||||
close_socket(sock);
|
||||
return -1;
|
||||
}
|
||||
memcpy(&addr.sin_addr, he->h_addr_list[0], he->h_length);
|
||||
}
|
||||
|
||||
/* 执行连接测试 */
|
||||
result = connect(sock, (struct sockaddr*)&addr, sizeof(addr));
|
||||
|
||||
/* 关闭socket */
|
||||
close_socket(sock);
|
||||
|
||||
/* 返回结果 */
|
||||
return (result == 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 扫描单个主机的多个端口
|
||||
*/
|
||||
int scan_host_ports(const char* host, const int* ports, int port_count, int timeout) {
|
||||
int i;
|
||||
int open_count = 0;
|
||||
|
||||
if (!host || !ports || port_count <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("Scanning %s...\n", host);
|
||||
|
||||
for (i = 0; i < port_count; i++) {
|
||||
int result = tcp_connect_test(host, ports[i], timeout);
|
||||
|
||||
if (result == 1) {
|
||||
printf("%s:%d open\n", host, ports[i]);
|
||||
open_count++;
|
||||
} else if (result == -1) {
|
||||
/* 静默处理错误,继续扫描 */
|
||||
}
|
||||
|
||||
/* 简单的进度指示 */
|
||||
if ((i + 1) % 100 == 0 || i == port_count - 1) {
|
||||
printf("Progress: %d/%d ports scanned\n", i + 1, port_count);
|
||||
}
|
||||
}
|
||||
|
||||
return open_count;
|
||||
}
|
||||
|
||||
/*
|
||||
* 解析端口范围字符串
|
||||
* 支持: "80", "80,443", "1-1000", "80,443,8000-8080"
|
||||
*/
|
||||
int parse_ports(const char* port_str, int* ports, int max_ports) {
|
||||
char* str_copy;
|
||||
char* token;
|
||||
int count = 0;
|
||||
|
||||
if (!port_str || !ports || max_ports <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 复制字符串以便修改 */
|
||||
str_copy = malloc(strlen(port_str) + 1);
|
||||
if (!str_copy) {
|
||||
return 0;
|
||||
}
|
||||
strcpy(str_copy, port_str);
|
||||
|
||||
/* 使用strtok(兼容性更好) */
|
||||
token = strtok(str_copy, ",");
|
||||
|
||||
while (token && count < max_ports) {
|
||||
char* dash = strchr(token, '-');
|
||||
|
||||
if (dash) {
|
||||
/* 处理范围 "start-end" */
|
||||
int start, end, i;
|
||||
*dash = '\0';
|
||||
start = atoi(token);
|
||||
end = atoi(dash + 1);
|
||||
|
||||
if (start > 0 && end > 0 && start <= end && end <= 65535) {
|
||||
for (i = start; i <= end && count < max_ports; i++) {
|
||||
ports[count++] = i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* 处理单个端口 */
|
||||
int port = atoi(token);
|
||||
if (port > 0 && port <= 65535) {
|
||||
ports[count++] = port;
|
||||
}
|
||||
}
|
||||
|
||||
token = strtok(NULL, ",");
|
||||
}
|
||||
|
||||
free(str_copy);
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* 简单的IP范围解析
|
||||
* 目前只支持单个IP,后续可扩展
|
||||
*/
|
||||
int parse_hosts(const char* host_str, char hosts[][MAX_HOST_LEN], int max_hosts) {
|
||||
if (!host_str || !hosts || max_hosts <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 目前简化实现:只处理单个主机 */
|
||||
if (strlen(host_str) < MAX_HOST_LEN) {
|
||||
strcpy(hosts[0], host_str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user