Skip to content

Commit a66de1b

Browse files
authored
feat: tcp端口扫描支持socks5 (#527)
* feat: tcp端口扫描支持socks5 * feat: PG插件支持socks5 * feat: 完成大部分插件的socks5支持
1 parent 9b38dc0 commit a66de1b

19 files changed

Lines changed: 374 additions & 94 deletions

Common/Proxy.go

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package Common
22

33
import (
4+
"context"
5+
"crypto/tls"
46
"errors"
57
"fmt"
6-
"golang.org/x/net/proxy"
78
"net"
89
"net/url"
910
"strings"
1011
"time"
12+
13+
"golang.org/x/net/proxy"
1114
)
1215

1316
// WrapperTcpWithTimeout 创建一个带超时的TCP连接
@@ -16,6 +19,12 @@ func WrapperTcpWithTimeout(network, address string, timeout time.Duration) (net.
1619
return WrapperTCP(network, address, d)
1720
}
1821

22+
// WrapperTcpWithContext 创建一个带上下文的TCP连接
23+
func WrapperTcpWithContext(ctx context.Context, network, address string) (net.Conn, error) {
24+
d := &net.Dialer{}
25+
return WrapperTCPWithContext(ctx, network, address, d)
26+
}
27+
1928
// WrapperTCP 根据配置创建TCP连接
2029
func WrapperTCP(network, address string, forward *net.Dialer) (net.Conn, error) {
2130
// 直连模式
@@ -41,6 +50,54 @@ func WrapperTCP(network, address string, forward *net.Dialer) (net.Conn, error)
4150
return conn, nil
4251
}
4352

53+
// WrapperTCPWithContext 根据配置创建支持上下文的TCP连接
54+
func WrapperTCPWithContext(ctx context.Context, network, address string, forward *net.Dialer) (net.Conn, error) {
55+
// 直连模式
56+
if Socks5Proxy == "" {
57+
conn, err := forward.DialContext(ctx, network, address)
58+
if err != nil {
59+
return nil, fmt.Errorf(GetText("tcp_conn_failed"), err)
60+
}
61+
return conn, nil
62+
}
63+
64+
// Socks5代理模式
65+
dialer, err := Socks5Dialer(forward)
66+
if err != nil {
67+
return nil, fmt.Errorf(GetText("socks5_create_failed"), err)
68+
}
69+
70+
// 创建一个结果通道来处理连接和取消
71+
connChan := make(chan struct {
72+
conn net.Conn
73+
err error
74+
}, 1)
75+
76+
go func() {
77+
conn, err := dialer.Dial(network, address)
78+
select {
79+
case <-ctx.Done():
80+
if conn != nil {
81+
conn.Close()
82+
}
83+
case connChan <- struct {
84+
conn net.Conn
85+
err error
86+
}{conn, err}:
87+
}
88+
}()
89+
90+
select {
91+
case <-ctx.Done():
92+
return nil, ctx.Err()
93+
case result := <-connChan:
94+
if result.err != nil {
95+
return nil, fmt.Errorf(GetText("socks5_conn_failed"), result.err)
96+
}
97+
return result.conn, nil
98+
}
99+
}
100+
44101
// Socks5Dialer 创建Socks5代理拨号器
45102
func Socks5Dialer(forward *net.Dialer) (proxy.Dialer, error) {
46103
// 解析代理URL
@@ -76,3 +133,59 @@ func Socks5Dialer(forward *net.Dialer) (proxy.Dialer, error) {
76133

77134
return dialer, nil
78135
}
136+
137+
// WrapperTlsWithContext 创建一个通过代理的TLS连接
138+
func WrapperTlsWithContext(ctx context.Context, network, address string, tlsConfig *tls.Config) (net.Conn, error) {
139+
// 直连模式
140+
if Socks5Proxy == "" {
141+
dialer := &net.Dialer{}
142+
143+
tcpConn, err := dialer.DialContext(ctx, network, address)
144+
if err != nil {
145+
return nil, fmt.Errorf("直连TCP连接失败: %v", err)
146+
}
147+
148+
// 在TCP连接上进行TLS握手
149+
tlsConn := tls.Client(tcpConn, tlsConfig)
150+
151+
// 使用ctx的deadline设置TLS握手超时
152+
if deadline, ok := ctx.Deadline(); ok {
153+
tlsConn.SetDeadline(deadline)
154+
}
155+
156+
if err := tlsConn.Handshake(); err != nil {
157+
tcpConn.Close()
158+
return nil, fmt.Errorf("TLS握手失败: %v", err)
159+
}
160+
161+
// 清除deadline,让上层代码自己管理超时
162+
tlsConn.SetDeadline(time.Time{})
163+
164+
return tlsConn, nil
165+
}
166+
167+
// Socks5代理模式
168+
// 首先通过代理建立到目标的TCP连接
169+
tcpConn, err := WrapperTcpWithContext(ctx, network, address)
170+
if err != nil {
171+
return nil, fmt.Errorf("通过代理建立TCP连接失败: %v", err)
172+
}
173+
174+
// 在TCP连接上进行TLS握手
175+
tlsConn := tls.Client(tcpConn, tlsConfig)
176+
177+
// 使用ctx的deadline设置TLS握手超时
178+
if deadline, ok := ctx.Deadline(); ok {
179+
tlsConn.SetDeadline(deadline)
180+
}
181+
182+
if err := tlsConn.Handshake(); err != nil {
183+
tcpConn.Close()
184+
return nil, fmt.Errorf("TLS握手失败: %v", err)
185+
}
186+
187+
// 清除deadline,让上层代码自己管理超时
188+
tlsConn.SetDeadline(time.Time{})
189+
190+
return tlsConn, nil
191+
}

Core/PortInfo.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,8 +407,8 @@ func (i *Info) Write(msg []byte) error {
407407
_, err := i.Conn.Write(msg)
408408
if err != nil && strings.Contains(err.Error(), "close") {
409409
i.Conn.Close()
410-
// 连接关闭时重试
411-
i.Conn, err = net.DialTimeout("tcp4", fmt.Sprintf("%s:%d", i.Address, i.Port), time.Duration(6)*time.Second)
410+
// 连接关闭时重试 - 支持SOCKS5代理
411+
i.Conn, err = Common.WrapperTcpWithTimeout("tcp", fmt.Sprintf("%s:%d", i.Address, i.Port), time.Duration(6)*time.Second)
412412
if err == nil {
413413
i.Conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(WrTimeout)))
414414
_, err = i.Conn.Write(msg)

Core/PortScan.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"github.com/shadow1ng/fscan/Common"
77
"golang.org/x/sync/errgroup"
88
"golang.org/x/sync/semaphore"
9-
"net"
109
"strings"
1110
"sync"
1211
"sync/atomic"
@@ -53,8 +52,8 @@ func EnhancedPortScan(hosts []string, ports string, timeout int64) []string {
5352
g.Go(func() error {
5453
defer sem.Release(1)
5554

56-
// 连接测试
57-
conn, err := net.DialTimeout("tcp", addr, to)
55+
// 连接测试 - 支持SOCKS5代理
56+
conn, err := Common.WrapperTcpWithTimeout("tcp", addr, to)
5857
if err != nil {
5958
return nil
6059
}

Plugins/ActiveMQ.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ package Plugins
33
import (
44
"context"
55
"fmt"
6-
"github.com/shadow1ng/fscan/Common"
7-
"net"
86
"strings"
97
"sync"
108
"time"
9+
10+
"github.com/shadow1ng/fscan/Common"
1111
)
1212

1313
// ActiveMQCredential 表示一个ActiveMQ凭据
@@ -213,8 +213,7 @@ func ActiveMQConn(ctx context.Context, info *Common.HostInfo, user string, pass
213213
addr := fmt.Sprintf("%s:%v", info.Host, info.Ports)
214214

215215
// 使用上下文创建带超时的连接
216-
dialer := &net.Dialer{Timeout: time.Duration(Common.Timeout) * time.Second}
217-
conn, err := dialer.DialContext(ctx, "tcp", addr)
216+
conn, err := Common.WrapperTcpWithTimeout("tcp", addr, time.Duration(Common.Timeout)*time.Second)
218217
if err != nil {
219218
return false, err
220219
}

Plugins/Cassandra.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,29 @@ package Plugins
33
import (
44
"context"
55
"fmt"
6-
"github.com/gocql/gocql"
7-
"github.com/shadow1ng/fscan/Common"
6+
"net"
87
"strconv"
98
"strings"
109
"sync"
1110
"time"
11+
12+
"github.com/gocql/gocql"
13+
"github.com/shadow1ng/fscan/Common"
1214
)
1315

16+
// CassandraProxyDialer 实现gocql.Dialer接口,支持代理连接
17+
type CassandraProxyDialer struct {
18+
timeout time.Duration
19+
}
20+
21+
func (d *CassandraProxyDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
22+
host, port, err := net.SplitHostPort(addr)
23+
if err != nil {
24+
return nil, err
25+
}
26+
return Common.WrapperTcpWithContext(ctx, network, fmt.Sprintf("%s:%s", host, port))
27+
}
28+
1429
// CassandraCredential 表示一个Cassandra凭据
1530
type CassandraCredential struct {
1631
Username string
@@ -223,6 +238,13 @@ func CassandraConn(ctx context.Context, info *Common.HostInfo, user string, pass
223238
cluster.ProtoVersion = 4
224239
cluster.Consistency = gocql.One
225240

241+
// 如果配置了代理,设置自定义Dialer
242+
if Common.Socks5Proxy != "" {
243+
cluster.Dialer = &CassandraProxyDialer{
244+
timeout: timeout,
245+
}
246+
}
247+
226248
if user != "" || pass != "" {
227249
cluster.Authenticator = gocql.PasswordAuthenticator{
228250
Username: user,

Plugins/IMAP.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import (
55
"context"
66
"crypto/tls"
77
"fmt"
8-
"github.com/shadow1ng/fscan/Common"
98
"io"
109
"net"
1110
"strings"
1211
"sync"
1312
"time"
13+
14+
"github.com/shadow1ng/fscan/Common"
1415
)
1516

1617
// IMAPCredential 表示一个IMAP凭据
@@ -211,8 +212,7 @@ func IMAPConn(ctx context.Context, info *Common.HostInfo, user string, pass stri
211212
// 在协程中尝试连接
212213
go func() {
213214
// 先尝试普通连接
214-
dialer := &net.Dialer{Timeout: timeout}
215-
conn, err := dialer.DialContext(ctx, "tcp", addr)
215+
conn, err := Common.WrapperTcpWithContext(ctx, "tcp", addr)
216216
if err == nil {
217217
flag, authErr := tryIMAPAuth(conn, user, pass, timeout)
218218
conn.Close()
@@ -232,14 +232,16 @@ func IMAPConn(ctx context.Context, info *Common.HostInfo, user string, pass stri
232232
tlsConfig := &tls.Config{
233233
InsecureSkipVerify: true,
234234
}
235-
tlsConn, tlsErr := tls.DialWithDialer(dialer, "tcp", addr, tlsConfig)
235+
236+
// 使用支持代理的TLS连接
237+
tlsConn, tlsErr := Common.WrapperTlsWithContext(ctx, "tcp", addr, tlsConfig)
236238
if tlsErr != nil {
237239
select {
238240
case <-ctx.Done():
239241
case resultChan <- struct {
240242
success bool
241243
err error
242-
}{false, fmt.Errorf("连接失败: %v", tlsErr)}:
244+
}{false, fmt.Errorf("TLS连接失败: %v", tlsErr)}:
243245
}
244246
return
245247
}

Plugins/LDAP.go

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ package Plugins
33
import (
44
"context"
55
"fmt"
6-
"github.com/go-ldap/ldap/v3"
7-
"github.com/shadow1ng/fscan/Common"
8-
"net"
96
"strings"
107
"sync"
118
"time"
9+
10+
"github.com/go-ldap/ldap/v3"
11+
"github.com/shadow1ng/fscan/Common"
1212
)
1313

1414
// LDAPCredential 表示一个LDAP凭据
@@ -210,13 +210,8 @@ func tryLDAPCredential(ctx context.Context, info *Common.HostInfo, credential LD
210210
func LDAPConn(ctx context.Context, info *Common.HostInfo, user string, pass string) (bool, error) {
211211
address := fmt.Sprintf("%s:%s", info.Host, info.Ports)
212212

213-
// 创建拨号器并设置超时
214-
dialer := &net.Dialer{
215-
Timeout: time.Duration(Common.Timeout) * time.Second,
216-
}
217-
218213
// 使用上下文控制的拨号过程
219-
conn, err := dialer.DialContext(ctx, "tcp", address)
214+
conn, err := Common.WrapperTcpWithContext(ctx, "tcp", address)
220215
if err != nil {
221216
return false, err
222217
}

0 commit comments

Comments
 (0)