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:
ZacharyZcR 2025-09-01 23:50:32 +00:00
parent a10153911e
commit a3177b28a6
46 changed files with 1123 additions and 517 deletions

View File

@ -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 // 全部端口
)

View File

@ -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()
}

View File

@ -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)

View File

@ -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

View File

@ -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" // 扫描探测插件
)

View File

@ -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() {
// 定义暗绿色系

View File

@ -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
// =============================================================================
// 核心扫描配置
// =============================================================================
ScanMode string = DefaultScanMode // 扫描模式
ThreadNum int = DefaultThreadNum // 线程数
Timeout int64 = DefaultTimeout // 超时时间
DisablePing bool = false // 禁用ping
LocalMode bool = false // 本地模式
LocalPlugin string // 本地插件选择
AliveOnly bool // 仅存活探测模式
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包变量
// =============================================================================
var (
Username string // 直接映射到base.Username
Password string // 直接映射到base.Password
Userdict map[string][]string // 直接映射到base.Userdict
Passwords []string // 直接映射到base.Passwords
)
// =============================================================================
// 网络配置 - 直接使用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
}
// 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
// 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)
}
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)
)
// 从config模块获取字典和映射
if serviceDict := config.GetGlobalServiceDict(); serviceDict != nil {
Userdict = serviceDict.GetAllUserDicts()
Passwords = serviceDict.GetPasswords()
}
// =============================================================================
// 初始化
// =============================================================================
func init() {
// 初始化core包配置
base.InitGlobalConfig()
// 从core包同步初始配置
SyncFromCore()
// 初始化向后兼容的时间变量
StartTime = startTimeInit
if probeMapping := config.GetGlobalProbeMapping(); probeMapping != nil {
PortMap = probeMapping.GetAllPortMappings()
DefaultMap = probeMapping.GetDefaultProbes()
}
})
}
// init 自动初始化
func init() {
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中定义此处不重复

View File

@ -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

View File

@ -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",
}
}

View File

@ -136,7 +136,14 @@ func (tp *TargetParser) Parse(input *TargetInput, options *ParserOptions) (*Pars
// 更新配置
result.Config.Targets.Hosts = hosts
result.Config.Targets.URLs = urls
// 如果存在明确的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,6 +169,35 @@ func (tp *TargetParser) parseHosts(input *TargetInput) ([]string, []error, []str
// 解析命令行主机
if input.Host != "" {
// 检查是否为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 {
// 普通主机,正常处理
hostList, err := tp.parseHostList(input.Host)
if err != nil {
errors = append(errors, NewParseError(ErrorTypeHostError, err.Error(), "command line", 0, err))
@ -169,6 +205,7 @@ func (tp *TargetParser) parseHosts(input *TargetInput) ([]string, []error, []str
hosts = append(hosts, hostList...)
}
}
}
// 从文件读取主机
if input.HostsFile != "" {

10
common/ports.go Normal file
View 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
View 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
}

View File

@ -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,20 +88,51 @@ func (b *BaseScanStrategy) IsPluginApplicableByName(pluginName string, targetHos
return true
}
// 本地插件特殊处理:优先检查,避免不必要的端口获取
// 应用过滤器类型检查,确保与传统接口逻辑一致
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) {
// 检查用户是否指定了具体端口范围
if common.Ports != "" && common.Ports != "all" {
// 用户指定了端口进行实际HTTP协议检测而不是预定义端口匹配
return b.isWebServicePortByProtocol(targetHost, targetPort)
} else {
// 用户未指定端口,使用智能检测(包含预定义端口)
return b.isWebServicePort(targetHost, targetPort)
}
}
// 无端口限制的其他插件适用于所有端口
if len(pluginPorts) == 0 {
@ -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 == "" {

View File

@ -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
View 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

View 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

Binary file not shown.

BIN
fscan-lite/platform.o Normal file

Binary file not shown.

BIN
fscan-lite/scanner.o Normal file

Binary file not shown.

120
fscan-lite/src/main.c Normal file
View 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
View 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.1Win95/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
View 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;
}

View File

@ -3,6 +3,7 @@ package main
import (
"fmt"
"os"
"time"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/core"
@ -15,6 +16,9 @@ import (
func main() {
// Linus式简化直接执行删除过度工程
// 首先设置开始时间
common.StartTime = time.Now()
var info common.HostInfo
common.Flag(&info)