fscan/plugins/services/oracle/connector.go
ZacharyZcR 4a3f281b6b refactor: 统一Plugins目录大小写为小写
- 将所有Plugins路径重命名为plugins
- 修复Git索引与实际文件系统大小写不一致问题
- 确保跨平台兼容性和路径一致性
2025-08-12 13:08:06 +08:00

207 lines
5.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package oracle
import (
"context"
"database/sql"
"fmt"
"strings"
"time"
_ "github.com/sijms/go-ora/v2"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/plugins/base"
)
// OracleConnection Oracle连接包装器
type OracleConnection struct {
db *sql.DB
target string
info string
serviceName string
}
// OracleConnector Oracle连接器实现
type OracleConnector struct{}
// NewOracleConnector 创建Oracle连接器
func NewOracleConnector() *OracleConnector {
return &OracleConnector{}
}
// Connect 连接到Oracle服务器不进行认证
func (c *OracleConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
// 尝试建立连接但不进行认证,使用空凭据进行连接尝试
db, dbInfo, serviceName, err := c.createConnection(ctx, info.Host, info.Ports, "", "", "")
if err != nil {
// 检查是否是Oracle服务相关错误
if c.isOracleError(err) {
// 即使连接失败但可以识别为Oracle服务
return &OracleConnection{
db: nil,
target: target,
info: "Oracle Database (Service Detected)",
serviceName: "",
}, nil
}
return nil, err
}
return &OracleConnection{
db: db,
target: target,
info: dbInfo,
serviceName: serviceName,
}, nil
}
// Authenticate 使用凭据进行认证
func (c *OracleConnector) Authenticate(ctx context.Context, conn interface{}, cred *base.Credential) error {
oracleConn, ok := conn.(*OracleConnection)
if !ok {
return fmt.Errorf("invalid connection type")
}
// 解析目标地址
parts := strings.Split(oracleConn.target, ":")
if len(parts) != 2 {
return fmt.Errorf("invalid target format")
}
host := parts[0]
port := parts[1]
// 使用提供的凭据创建新连接
db, info, serviceName, err := c.createConnection(ctx, host, port, cred.Username, cred.Password, "")
if err != nil {
return err
}
// 更新连接信息
if oracleConn.db != nil {
oracleConn.db.Close()
}
oracleConn.db = db
oracleConn.info = info
oracleConn.serviceName = serviceName
return nil
}
// Close 关闭连接
func (c *OracleConnector) Close(conn interface{}) error {
if oracleConn, ok := conn.(*OracleConnection); ok && oracleConn.db != nil {
return oracleConn.db.Close()
}
return nil
}
// createConnection 创建Oracle数据库连接
func (c *OracleConnector) createConnection(ctx context.Context, host, port, username, password, serviceName string) (*sql.DB, string, string, error) {
timeout := time.Duration(common.Timeout) * time.Second
// 常见Oracle服务名列表
commonServiceNames := []string{"XE", "ORCL", "ORCLPDB1", "XEPDB1", "PDBORCL"}
// 如果未指定服务名,尝试所有常见服务名
serviceNamesToTry := []string{serviceName}
if serviceName == "" {
serviceNamesToTry = commonServiceNames
}
var lastErr error
for _, svcName := range serviceNamesToTry {
if svcName == "" {
continue
}
// 构造连接字符串
connStr := fmt.Sprintf("oracle://%s:%s@%s:%s/%s?connect_timeout=%d",
username, password, host, port, svcName, int(timeout.Seconds()))
// 对SYS用户使用SYSDBA权限
if strings.ToUpper(username) == "SYS" {
connStr += "&sysdba=1"
}
// 建立数据库连接
db, err := sql.Open("oracle", connStr)
if err != nil {
lastErr = err
continue
}
// 设置连接参数
db.SetConnMaxLifetime(timeout)
db.SetConnMaxIdleTime(timeout)
db.SetMaxIdleConns(0)
db.SetMaxOpenConns(1)
// 创建ping上下文
pingCtx, pingCancel := context.WithTimeout(ctx, timeout)
// 测试连接
err = db.PingContext(pingCtx)
pingCancel()
if err != nil {
db.Close()
lastErr = err
// 如果是认证错误,继续尝试下一个服务名
if strings.Contains(err.Error(), "ORA-01017") {
continue
}
continue
}
// 获取数据库信息
info := c.getDatabaseInfo(db, ctx)
return db, info, svcName, nil
}
return nil, "", "", lastErr
}
// getDatabaseInfo 获取Oracle数据库信息
func (c *OracleConnector) getDatabaseInfo(db *sql.DB, ctx context.Context) string {
var version string
err := db.QueryRowContext(ctx, "SELECT BANNER FROM V$VERSION WHERE ROWNUM = 1").Scan(&version)
if err != nil {
// 尝试其他查询
err = db.QueryRowContext(ctx, "SELECT VERSION FROM PRODUCT_COMPONENT_VERSION WHERE PRODUCT LIKE 'Oracle%' AND ROWNUM = 1").Scan(&version)
if err != nil {
return "Oracle Database"
}
}
return version
}
// isOracleError 检查是否是Oracle相关错误
func (c *OracleConnector) isOracleError(err error) bool {
if err == nil {
return false
}
errorStr := strings.ToLower(err.Error())
oracleErrorIndicators := []string{
"ora-",
"oracle",
"tns:",
"listener",
"database",
"connection refused",
"invalid authorization specification",
"service name",
"sid",
}
for _, indicator := range oracleErrorIndicators {
if strings.Contains(errorStr, indicator) {
return true
}
}
return false
}