mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00

- 重构插件注册架构采用现代工厂模式和自动发现机制 - 新增完整的插件元数据管理系统支持版本能力标签等信息 - 实现智能插件适配器提供向后兼容的桥接功能 - 建立MySQL Redis SSH三个标准插件作为新架构参考实现 - 优化插件扫描逻辑支持按端口按类型的智能查询和过滤 - 添加国际化支持和完善的文档体系 - 代码量减少67%维护成本大幅降低扩展性显著提升 新架构特点: - 零配置插件注册import即用 - 工厂模式延迟初始化和依赖注入 - 丰富元数据系统和能力声明 - 完全解耦的模块化设计 - 面向未来的可扩展架构 测试验证: MySQL和Redis插件功能完整包括弱密码检测未授权访问检测和自动利用攻击
268 lines
7.6 KiB
Go
268 lines
7.6 KiB
Go
package main
|
||
|
||
import (
|
||
"context"
|
||
"database/sql"
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
|
||
_ "github.com/go-sql-driver/mysql"
|
||
)
|
||
|
||
// TestMySQLConnection 测试MySQL连接字符串的各种格式
|
||
func TestMySQLConnection() {
|
||
// 测试参数
|
||
host := "127.0.0.1"
|
||
port := 3306
|
||
username := "root"
|
||
password := "123456"
|
||
timeoutDuration := 3 * time.Second
|
||
timeoutStr := timeoutDuration.String() // "3s"
|
||
|
||
fmt.Println("=== MySQL连接字符串测试 ===")
|
||
fmt.Printf("目标: %s:%d\n", host, port)
|
||
fmt.Printf("用户: %s\n", username)
|
||
fmt.Printf("超时: %s\n", timeoutStr)
|
||
fmt.Println()
|
||
|
||
// 测试不同的连接字符串格式
|
||
testConfigs := []struct {
|
||
name string
|
||
dsn string
|
||
desc string
|
||
}{
|
||
{
|
||
name: "当前fscan格式",
|
||
dsn: fmt.Sprintf("%s:%s@tcp(%s:%d)/mysql?charset=utf8&timeout=%s", username, password, host, port, timeoutStr),
|
||
desc: "fscan当前使用的格式,包含数据库名mysql",
|
||
},
|
||
{
|
||
name: "不指定数据库",
|
||
dsn: fmt.Sprintf("%s:%s@tcp(%s:%d)/?charset=utf8&timeout=%s", username, password, host, port, timeoutStr),
|
||
desc: "不指定具体数据库,连接到默认数据库",
|
||
},
|
||
{
|
||
name: "无数据库名",
|
||
dsn: fmt.Sprintf("%s:%s@tcp(%s:%d)?charset=utf8&timeout=%s", username, password, host, port, timeoutStr),
|
||
desc: "完全不指定数据库名",
|
||
},
|
||
{
|
||
name: "标准格式+readTimeout",
|
||
dsn: fmt.Sprintf("%s:%s@tcp(%s:%d)/mysql?charset=utf8&timeout=%s&readTimeout=%s&writeTimeout=%s", username, password, host, port, timeoutStr, timeoutStr, timeoutStr),
|
||
desc: "添加读写超时参数",
|
||
},
|
||
{
|
||
name: "使用parseTime",
|
||
dsn: fmt.Sprintf("%s:%s@tcp(%s:%d)/mysql?charset=utf8&timeout=%s&parseTime=true", username, password, host, port, timeoutStr),
|
||
desc: "添加parseTime参数处理时间类型",
|
||
},
|
||
{
|
||
name: "最小参数",
|
||
dsn: fmt.Sprintf("%s:%s@tcp(%s:%d)/", username, password, host, port),
|
||
desc: "最简单的连接字符串",
|
||
},
|
||
}
|
||
|
||
for i, config := range testConfigs {
|
||
fmt.Printf("[%d] %s\n", i+1, config.name)
|
||
fmt.Printf("描述: %s\n", config.desc)
|
||
fmt.Printf("DSN: %s\n", config.dsn)
|
||
|
||
// 测试连接
|
||
success := testConnection(config.dsn, timeoutDuration)
|
||
|
||
if success {
|
||
fmt.Printf("结果: ✅ 连接成功\n")
|
||
} else {
|
||
fmt.Printf("结果: ❌ 连接失败\n")
|
||
}
|
||
fmt.Println(strings.Repeat("-", 80))
|
||
}
|
||
|
||
// 测试context超时的影响
|
||
fmt.Println("\n=== Context超时测试 ===")
|
||
testContextTimeout()
|
||
}
|
||
|
||
// testConnection 测试指定DSN的连接
|
||
func testConnection(dsn string, timeout time.Duration) bool {
|
||
// 创建context,设置超时时间
|
||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||
defer cancel()
|
||
|
||
// 尝试连接
|
||
db, err := sql.Open("mysql", dsn)
|
||
if err != nil {
|
||
fmt.Printf(" Open失败: %v\n", err)
|
||
return false
|
||
}
|
||
defer db.Close()
|
||
|
||
// 设置连接池参数
|
||
db.SetMaxOpenConns(1)
|
||
db.SetMaxIdleConns(1)
|
||
db.SetConnMaxLifetime(timeout)
|
||
|
||
// 测试ping - 这是实际建立连接的地方
|
||
err = db.PingContext(ctx)
|
||
if err != nil {
|
||
fmt.Printf(" Ping失败: %v\n", err)
|
||
return false
|
||
}
|
||
|
||
// 执行简单查询测试
|
||
var version string
|
||
err = db.QueryRowContext(ctx, "SELECT VERSION()").Scan(&version)
|
||
if err != nil {
|
||
fmt.Printf(" 查询失败: %v\n", err)
|
||
return false
|
||
}
|
||
|
||
fmt.Printf(" MySQL版本: %s\n", version)
|
||
return true
|
||
}
|
||
|
||
// testContextTimeout 测试context超时对连接的影响
|
||
func testContextTimeout() {
|
||
host := "127.0.0.1"
|
||
port := 3306
|
||
username := "root"
|
||
password := "123456"
|
||
|
||
// 基础连接字符串(不设置timeout参数)
|
||
baseDSN := fmt.Sprintf("%s:%s@tcp(%s:%d)/mysql?charset=utf8", username, password, host, port)
|
||
|
||
timeoutTests := []struct {
|
||
name string
|
||
dsn string
|
||
contextTimeout time.Duration
|
||
desc string
|
||
}{
|
||
{
|
||
name: "仅Context超时3s",
|
||
dsn: baseDSN,
|
||
contextTimeout: 3 * time.Second,
|
||
desc: "只使用context超时,不在DSN中设置timeout",
|
||
},
|
||
{
|
||
name: "DSN超时3s + Context超时3s",
|
||
dsn: baseDSN + "&timeout=3s",
|
||
contextTimeout: 3 * time.Second,
|
||
desc: "同时设置DSN和context超时",
|
||
},
|
||
{
|
||
name: "DSN超时10s + Context超时3s",
|
||
dsn: baseDSN + "&timeout=10s",
|
||
contextTimeout: 3 * time.Second,
|
||
desc: "DSN超时更长,context超时更短",
|
||
},
|
||
{
|
||
name: "DSN超时3s + Context超时10s",
|
||
dsn: baseDSN + "&timeout=3s",
|
||
contextTimeout: 10 * time.Second,
|
||
desc: "DSN超时更短,context超时更长",
|
||
},
|
||
}
|
||
|
||
for i, test := range timeoutTests {
|
||
fmt.Printf("[%d] %s\n", i+1, test.name)
|
||
fmt.Printf("描述: %s\n", test.desc)
|
||
fmt.Printf("DSN: %s\n", test.dsn)
|
||
fmt.Printf("Context超时: %v\n", test.contextTimeout)
|
||
|
||
start := time.Now()
|
||
success := testConnectionWithTiming(test.dsn, test.contextTimeout)
|
||
elapsed := time.Since(start)
|
||
|
||
if success {
|
||
fmt.Printf("结果: ✅ 连接成功,耗时: %v\n", elapsed)
|
||
} else {
|
||
fmt.Printf("结果: ❌ 连接失败,耗时: %v\n", elapsed)
|
||
}
|
||
fmt.Println(strings.Repeat("-", 80))
|
||
}
|
||
}
|
||
|
||
// testConnectionWithTiming 带时间统计的连接测试
|
||
func testConnectionWithTiming(dsn string, contextTimeout time.Duration) bool {
|
||
ctx, cancel := context.WithTimeout(context.Background(), contextTimeout)
|
||
defer cancel()
|
||
|
||
db, err := sql.Open("mysql", dsn)
|
||
if err != nil {
|
||
fmt.Printf(" Open失败: %v\n", err)
|
||
return false
|
||
}
|
||
defer db.Close()
|
||
|
||
db.SetMaxOpenConns(1)
|
||
db.SetMaxIdleConns(1)
|
||
|
||
err = db.PingContext(ctx)
|
||
if err != nil {
|
||
fmt.Printf(" Ping失败: %v\n", err)
|
||
return false
|
||
}
|
||
|
||
return true
|
||
}
|
||
|
||
// 优化建议和分析
|
||
func printOptimizationSuggestions() {
|
||
fmt.Println("\n=== MySQL连接字符串优化建议 ===")
|
||
|
||
suggestions := []string{
|
||
"1. 连接字符串格式建议:",
|
||
" 推荐: user:pass@tcp(host:port)/dbname?charset=utf8mb4&parseTime=true&timeout=3s",
|
||
"",
|
||
"2. 超时参数优化:",
|
||
" - 使用charset=utf8mb4而不是utf8,支持完整的UTF-8字符集",
|
||
" - 添加parseTime=true自动解析时间类型",
|
||
" - 分别设置timeout、readTimeout、writeTimeout更精细控制",
|
||
"",
|
||
"3. Context vs DSN超时:",
|
||
" - Context超时控制整个操作的最大时间",
|
||
" - DSN timeout参数控制连接建立的超时",
|
||
" - 建议Context超时 >= DSN timeout + 额外处理时间",
|
||
"",
|
||
"4. 连接池优化:",
|
||
" - SetMaxOpenConns(1) 对于扫描场景是合适的",
|
||
" - SetConnMaxLifetime应该设置适当的值避免连接泄露",
|
||
" - 使用defer db.Close()确保连接被释放",
|
||
"",
|
||
"5. 错误处理:",
|
||
" - 'context deadline exceeded' 通常表示网络问题或超时设置过短",
|
||
" - 检查防火墙设置和网络连接",
|
||
" - 确认MySQL服务器配置允许远程连接",
|
||
}
|
||
|
||
for _, suggestion := range suggestions {
|
||
fmt.Println(suggestion)
|
||
}
|
||
}
|
||
|
||
func main() {
|
||
fmt.Println("MySQL连接字符串测试工具")
|
||
fmt.Println("作者: Go语言代码优化专家")
|
||
fmt.Println("目的: 验证fscan中MySQL连接字符串格式的正确性")
|
||
fmt.Println("=" + strings.Repeat("=", 60))
|
||
|
||
// 检查必要的包
|
||
fmt.Println("\n检查依赖...")
|
||
fmt.Println("✅ github.com/go-sql-driver/mysql 已导入")
|
||
|
||
// 执行测试
|
||
TestMySQLConnection()
|
||
|
||
// 打印优化建议
|
||
printOptimizationSuggestions()
|
||
|
||
fmt.Println("\n测试完成!")
|
||
fmt.Println("\n使用方法:")
|
||
fmt.Println("1. 确保MySQL服务器运行在127.0.0.1:3306")
|
||
fmt.Println("2. 创建用户: CREATE USER 'root'@'%' IDENTIFIED BY '123456';")
|
||
fmt.Println("3. 授权: GRANT ALL PRIVILEGES ON *.* TO 'root'@'%';")
|
||
fmt.Println("4. 刷新权限: FLUSH PRIVILEGES;")
|
||
fmt.Println("5. 运行: go run mysql_connection_test.go")
|
||
} |