mirror of
https://github.com/bolucat/Archive.git
synced 2025-12-24 13:28:37 +08:00
Update On Wed Nov 5 19:38:45 CET 2025
This commit is contained in:
@@ -1582,4 +1582,5 @@ Examples:
|
||||
| `rate` | Bandwidth rate limit | Integer (Mbps), 0=unlimited | `0` | Both |
|
||||
| `slot` | Connection slot count | Integer (1-65536) | `65536` | Both |
|
||||
| `proxy` | PROXY protocol support | `0`(disabled), `1`(enabled) | `0` | Both |
|
||||
| `notcp` | TCP support control | `0`(enabled), `1`(disabled) | `0` | Both |
|
||||
| `noudp` | UDP support control | `0`(enabled), `1`(disabled) | `0` | Both |
|
||||
@@ -262,6 +262,43 @@ nodepass "server://0.0.0.0:10101/0.0.0.0:8080?log=info&tls=1&proxy=1&rate=100"
|
||||
- The header format follows the HAProxy PROXY protocol v1 specification
|
||||
- If the target service doesn't support PROXY protocol, connections may fail or behave unexpectedly
|
||||
|
||||
## TCP Support Control
|
||||
|
||||
NodePass supports TCP traffic tunneling by default. The `notcp` parameter allows you to disable TCP support when only UDP traffic needs to be handled, which can reduce resource usage and simplify configuration.
|
||||
|
||||
- `notcp`: TCP support control (default: 0)
|
||||
- Value 0: TCP support enabled - both TCP and UDP traffic will be tunneled
|
||||
- Value 1: TCP support disabled - only UDP traffic will be tunneled, TCP connections are ignored
|
||||
- Applies to both client and server modes
|
||||
- When disabled, TCP-related resources (buffers, connections, sessions) are not allocated
|
||||
|
||||
Example:
|
||||
```bash
|
||||
# Enable TCP support (default behavior)
|
||||
nodepass "server://0.0.0.0:10101/0.0.0.0:8080?notcp=0"
|
||||
|
||||
# Disable TCP support for UDP-only scenarios
|
||||
nodepass "server://0.0.0.0:10101/0.0.0.0:8080?notcp=1"
|
||||
|
||||
# Client with TCP disabled
|
||||
nodepass "client://server.example.com:10101/127.0.0.1:8080?notcp=1"
|
||||
|
||||
# Combined with other parameters
|
||||
nodepass "server://0.0.0.0:10101/0.0.0.0:8080?log=info&tls=1¬cp=1"
|
||||
```
|
||||
|
||||
**TCP Support Control Use Cases:**
|
||||
- **UDP-Only Services**: Disable TCP when tunneling only UDP-based applications
|
||||
- **Resource Optimization**: Reduce memory and CPU usage by avoiding TCP processing overhead
|
||||
- **Security**: Prevent TCP-based attacks or unwanted traffic in restricted environments
|
||||
- **Simplified Configuration**: Easier setup when TCP tunneling is not required
|
||||
- **Network Isolation**: Isolate TCP and UDP traffic handling for better control
|
||||
|
||||
**Important Notes:**
|
||||
- When TCP is disabled, any TCP connections sent to the tunnel will be silently dropped
|
||||
- Existing TCP sessions will be terminated when switching to notcp=1
|
||||
- TCP buffer pools and session management are disabled when notcp=1
|
||||
|
||||
## UDP Support Control
|
||||
|
||||
NodePass supports UDP traffic tunneling in addition to TCP. The `noudp` parameter allows you to disable UDP support when only TCP traffic needs to be handled, which can reduce resource usage and simplify configuration.
|
||||
@@ -373,6 +410,7 @@ NodePass allows flexible configuration via URL query parameters. The following t
|
||||
| `rate` | Bandwidth rate limit | `0` | O | O | X |
|
||||
| `slot` | Maximum connection limit | `65536` | O | O | X |
|
||||
| `proxy` | PROXY protocol support| `0` | O | O | X |
|
||||
| `notcp` | TCP support control | `0` | O | O | X |
|
||||
| `noudp` | UDP support control | `0` | O | O | X |
|
||||
|
||||
- O: Parameter is valid and recommended for configuration
|
||||
@@ -383,6 +421,7 @@ NodePass allows flexible configuration via URL query parameters. The following t
|
||||
- For client/server dual-end handshake modes, adjust connection pool capacity (`min`, `max`) based on traffic and resource constraints for optimal performance.
|
||||
- Use run mode control (`mode`) when automatic detection doesn't match your deployment requirements or for consistent behavior across environments.
|
||||
- Configure rate limiting (`rate`) to control bandwidth usage and prevent network congestion in shared environments.
|
||||
- Set `notcp=1` when only UDP traffic needs to be tunneled to reduce resource usage and simplify configuration.
|
||||
- Set `noudp=1` when only TCP traffic needs to be tunneled to reduce resource usage and simplify configuration.
|
||||
- Log level (`log`) can be set in all modes for easier operations and troubleshooting.
|
||||
|
||||
|
||||
@@ -1582,4 +1582,5 @@ client://<server_host>:<server_port>/<local_host>:<local_port>?<parameters>
|
||||
| `rate` | 带宽速率限制 | 整数 (Mbps), 0=无限制 | `0` | 两者 |
|
||||
| `slot` | 连接槽位数 | 整数 (1-65536) | `65536` | 两者 |
|
||||
| `proxy` | PROXY协议支持 | `0`(禁用), `1`(启用) | `0` | 两者 |
|
||||
| `notcp` | TCP支持控制 | `0`(启用), `1`(禁用) | `0` | 两者 |
|
||||
| `noudp` | UDP支持控制 | `0`(启用), `1`(禁用) | `0` | 两者 |
|
||||
@@ -262,6 +262,43 @@ nodepass "server://0.0.0.0:10101/0.0.0.0:8080?log=info&tls=1&proxy=1&rate=100"
|
||||
- 头部格式遵循HAProxy PROXY协议v1规范
|
||||
- 如果目标服务不支持PROXY协议,将导致连接失败
|
||||
|
||||
## TCP支持控制
|
||||
|
||||
NodePass默认支持TCP流量隧道。`notcp`参数允许您在只需要处理UDP流量时禁用TCP支持,这样可以减少资源使用并简化配置。
|
||||
|
||||
- `notcp`: TCP支持控制(默认: 0)
|
||||
- 值为0:启用TCP支持 - TCP和UDP流量都将被隧道传输
|
||||
- 值为1:禁用TCP支持 - 仅UDP流量将被隧道传输,TCP连接被忽略
|
||||
- 适用于客户端和服务器模式
|
||||
- 禁用时,不分配TCP相关资源(缓冲区、连接、会话)
|
||||
|
||||
示例:
|
||||
```bash
|
||||
# 启用TCP支持(默认行为)
|
||||
nodepass "server://0.0.0.0:10101/0.0.0.0:8080?notcp=0"
|
||||
|
||||
# 禁用TCP支持,仅处理UDP场景
|
||||
nodepass "server://0.0.0.0:10101/0.0.0.0:8080?notcp=1"
|
||||
|
||||
# 客户端禁用TCP
|
||||
nodepass "client://server.example.com:10101/127.0.0.1:8080?notcp=1"
|
||||
|
||||
# 与其他参数结合
|
||||
nodepass "server://0.0.0.0:10101/0.0.0.0:8080?log=info&tls=1¬cp=1"
|
||||
```
|
||||
|
||||
**TCP支持控制使用场景:**
|
||||
- **仅UDP服务**:仅需要隧道传输UDP应用时禁用TCP
|
||||
- **资源优化**:通过避免TCP处理开销减少内存和CPU使用
|
||||
- **安全性**:防止受限环境中的TCP攻击或不需要的流量
|
||||
- **简化配置**:不需要TCP隧道时更容易设置
|
||||
- **网络隔离**:更好地控制TCP和UDP流量处理
|
||||
|
||||
**重要说明:**
|
||||
- 禁用TCP时,发送到隧道的任何TCP连接将被静默丢弃
|
||||
- 切换到notcp=1时,现有的TCP会话将被终止
|
||||
- 当notcp=1时,TCP缓冲池和会话管理被禁用
|
||||
|
||||
## UDP支持控制
|
||||
|
||||
除了TCP之外,NodePass还支持UDP流量隧道。`noudp`参数允许您在只需要处理TCP流量时禁用UDP支持,这样可以减少资源使用并简化配置。
|
||||
@@ -373,6 +410,7 @@ NodePass支持通过URL查询参数进行灵活配置,不同参数在 server
|
||||
| `rate` | 带宽速率限制 | `0` | O | O | X |
|
||||
| `slot` | 最大连接数限制 | `65536` | O | O | X |
|
||||
| `proxy` | PROXY协议支持 | `0` | O | O | X |
|
||||
| `notcp` | TCP支持控制 | `0` | O | O | X |
|
||||
| `noudp` | UDP支持控制 | `0` | O | O | X |
|
||||
|
||||
- O:参数有效,推荐根据实际场景配置
|
||||
@@ -383,6 +421,7 @@ NodePass支持通过URL查询参数进行灵活配置,不同参数在 server
|
||||
- client/server 双端握手模式建议根据流量和资源情况调整连接池容量(min/max),优化性能。
|
||||
- 当自动检测不符合部署需求时或需要跨环境一致行为时,使用运行模式控制(mode)。
|
||||
- 配置速率限制(rate)以控制带宽使用,防止共享环境中的网络拥塞。
|
||||
- 仅需要隧道传输UDP流量时设置`notcp=1`,以减少资源使用并简化配置。
|
||||
- 仅需要隧道传输TCP流量时设置`noudp=1`,以减少资源使用并简化配置。
|
||||
- 日志级别(log)可在所有模式下灵活调整,便于运维和排查。
|
||||
|
||||
|
||||
@@ -6,5 +6,5 @@ require (
|
||||
github.com/NodePassProject/cert v1.0.1
|
||||
github.com/NodePassProject/conn v1.0.16
|
||||
github.com/NodePassProject/logs v1.0.3
|
||||
github.com/NodePassProject/pool v1.0.48
|
||||
github.com/NodePassProject/pool v1.0.49
|
||||
)
|
||||
|
||||
@@ -4,5 +4,5 @@ github.com/NodePassProject/conn v1.0.16 h1:ojHfyBveZMcyOikdUs1SOW4yKp92NOBnNhfNe
|
||||
github.com/NodePassProject/conn v1.0.16/go.mod h1:xfQ7ZLUxrtdLsljGHYYCToW+Hdg6DAbmL1Cs94n5h6E=
|
||||
github.com/NodePassProject/logs v1.0.3 h1:CDUZVQ477vmmFQHazrQCWM0gJPNINm0C2N3FzC4jVyw=
|
||||
github.com/NodePassProject/logs v1.0.3/go.mod h1:TwtPXOzLtb8iH+fdduQjEEywICXivsM39cy9AinMSks=
|
||||
github.com/NodePassProject/pool v1.0.48 h1:99pCHQYtmH5sVIB0vY+KbV4zyWSH6ptHgkKtxDnjpqQ=
|
||||
github.com/NodePassProject/pool v1.0.48/go.mod h1:joQFk1oocg56QpJ1QK/2g5Jv/AyqYUQgPXMG1gWe8iA=
|
||||
github.com/NodePassProject/pool v1.0.49 h1:gktVmE+GsQ0/C0MF8qgRraR7eS3na4k0QrQfR6o4fkM=
|
||||
github.com/NodePassProject/pool v1.0.49/go.mod h1:joQFk1oocg56QpJ1QK/2g5Jv/AyqYUQgPXMG1gWe8iA=
|
||||
|
||||
@@ -45,7 +45,6 @@ func NewClient(parsedURL *url.URL, logger *logs.Logger) (*Client, error) {
|
||||
return &buf
|
||||
},
|
||||
},
|
||||
cleanURL: &url.URL{Scheme: "np", Fragment: "c"},
|
||||
flushURL: &url.URL{Scheme: "np", Fragment: "f"},
|
||||
pingURL: &url.URL{Scheme: "np", Fragment: "i"},
|
||||
pongURL: &url.URL{Scheme: "np", Fragment: "o"},
|
||||
@@ -62,9 +61,9 @@ func NewClient(parsedURL *url.URL, logger *logs.Logger) (*Client, error) {
|
||||
// Run 管理客户端生命周期
|
||||
func (c *Client) Run() {
|
||||
logInfo := func(prefix string) {
|
||||
c.logger.Info("%v: client://%v@%v/%v?min=%v&mode=%v&read=%v&rate=%v&slot=%v&proxy=%v&noudp=%v",
|
||||
c.logger.Info("%v: client://%v@%v/%v?min=%v&mode=%v&read=%v&rate=%v&slot=%v&proxy=%v¬cp=%v&noudp=%v",
|
||||
prefix, c.tunnelKey, c.tunnelTCPAddr, c.getTargetAddrsString(),
|
||||
c.minPoolCapacity, c.runMode, c.readTimeout, c.rateLimit/125000, c.slotLimit, c.proxyProtocol, c.disableUDP)
|
||||
c.minPoolCapacity, c.runMode, c.readTimeout, c.rateLimit/125000, c.slotLimit, c.proxyProtocol, c.disableTCP, c.disableUDP)
|
||||
}
|
||||
logInfo("Client started")
|
||||
|
||||
@@ -157,13 +156,15 @@ func (c *Client) commonStart() error {
|
||||
})
|
||||
go c.tunnelPool.ClientManager()
|
||||
|
||||
// 判断数据流向
|
||||
if c.dataFlow == "+" {
|
||||
// 初始化目标监听器
|
||||
if err := c.initTargetListener(); err != nil {
|
||||
return fmt.Errorf("commonStart: initTargetListener failed: %w", err)
|
||||
}
|
||||
go c.commonLoop()
|
||||
}
|
||||
|
||||
// 启动共用控制
|
||||
if err := c.commonControl(); err != nil {
|
||||
return fmt.Errorf("commonStart: commonControl failed: %w", err)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
@@ -29,6 +31,7 @@ type Common struct {
|
||||
mu sync.Mutex // 互斥锁
|
||||
logger *logs.Logger // 日志记录器
|
||||
tlsCode string // TLS模式代码
|
||||
tlsConfig *tls.Config // TLS配置
|
||||
runMode string // 运行模式
|
||||
dataFlow string // 数据流向
|
||||
tunnelKey string // 隧道密钥
|
||||
@@ -47,6 +50,7 @@ type Common struct {
|
||||
minPoolCapacity int // 最小池容量
|
||||
maxPoolCapacity int // 最大池容量
|
||||
proxyProtocol string // 代理协议
|
||||
disableTCP string // 禁用TCP
|
||||
disableUDP string // 禁用UDP
|
||||
rateLimit int // 速率限制
|
||||
rateLimiter *conn.RateLimiter // 全局限速器
|
||||
@@ -56,8 +60,6 @@ type Common struct {
|
||||
udpBufferPool *sync.Pool // UDP缓冲区池
|
||||
signalChan chan string // 信号通道
|
||||
checkPoint time.Time // 检查点时间
|
||||
lastClean time.Time // 上次清理时间
|
||||
cleanURL *url.URL // 清理信号
|
||||
flushURL *url.URL // 重置信号
|
||||
pingURL *url.URL // PING信号
|
||||
pongURL *url.URL // PONG信号
|
||||
@@ -99,6 +101,7 @@ const (
|
||||
defaultRateLimit = 0 // 默认速率限制
|
||||
defaultSlotLimit = 65536 // 默认槽位限制
|
||||
defaultProxyProtocol = "0" // 默认代理协议
|
||||
defaultTCPStrategy = "0" // 默认TCP策略
|
||||
defaultUDPStrategy = "0" // 默认UDP策略
|
||||
)
|
||||
|
||||
@@ -184,6 +187,22 @@ func getEnvAsDuration(name string, defaultValue time.Duration) time.Duration {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// formatCertFingerprint 格式化证书指纹为标准格式
|
||||
func (c *Common) formatCertFingerprint(certRaw []byte) string {
|
||||
hash := sha256.Sum256(certRaw)
|
||||
hashHex := hex.EncodeToString(hash[:])
|
||||
|
||||
var formatted strings.Builder
|
||||
for i := 0; i < len(hashHex); i += 2 {
|
||||
if i > 0 {
|
||||
formatted.WriteByte(':')
|
||||
}
|
||||
formatted.WriteString(strings.ToUpper(hashHex[i : i+2]))
|
||||
}
|
||||
|
||||
return "sha256:" + formatted.String()
|
||||
}
|
||||
|
||||
// xor 对数据进行异或处理
|
||||
func (c *Common) xor(data []byte) []byte {
|
||||
for i := range data {
|
||||
@@ -404,6 +423,15 @@ func (c *Common) getProxyProtocol(parsedURL *url.URL) {
|
||||
}
|
||||
}
|
||||
|
||||
// getTCPStrategy 获取TCP策略
|
||||
func (c *Common) getTCPStrategy(parsedURL *url.URL) {
|
||||
if tcpStrategy := parsedURL.Query().Get("notcp"); tcpStrategy != "" {
|
||||
c.disableTCP = tcpStrategy
|
||||
} else {
|
||||
c.disableTCP = defaultTCPStrategy
|
||||
}
|
||||
}
|
||||
|
||||
// getUDPStrategy 获取UDP策略
|
||||
func (c *Common) getUDPStrategy(parsedURL *url.URL) {
|
||||
if udpStrategy := parsedURL.Query().Get("noudp"); udpStrategy != "" {
|
||||
@@ -426,6 +454,7 @@ func (c *Common) initConfig(parsedURL *url.URL) error {
|
||||
c.getRateLimit(parsedURL)
|
||||
c.getSlotLimit(parsedURL)
|
||||
c.getProxyProtocol(parsedURL)
|
||||
c.getTCPStrategy(parsedURL)
|
||||
c.getUDPStrategy(parsedURL)
|
||||
|
||||
return nil
|
||||
@@ -490,7 +519,7 @@ func (c *Common) initTunnelListener() error {
|
||||
}
|
||||
|
||||
// 初始化隧道TCP监听器
|
||||
if c.tunnelTCPAddr != nil {
|
||||
if c.tunnelTCPAddr != nil && c.disableTCP != "1" {
|
||||
tunnelListener, err := net.ListenTCP("tcp", c.tunnelTCPAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("initTunnelListener: listenTCP failed: %w", err)
|
||||
@@ -517,7 +546,7 @@ func (c *Common) initTargetListener() error {
|
||||
}
|
||||
|
||||
// 初始化目标TCP监听器
|
||||
if len(c.targetTCPAddrs) > 0 {
|
||||
if len(c.targetTCPAddrs) > 0 && c.disableTCP != "1" {
|
||||
targetListener, err := net.ListenTCP("tcp", c.targetTCPAddrs[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("initTargetListener: listenTCP failed: %w", err)
|
||||
@@ -686,27 +715,20 @@ func (c *Common) healthCheck() error {
|
||||
ticker := time.NewTicker(reportInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
case <-ticker.C:
|
||||
c.incomingVerify()
|
||||
}
|
||||
}()
|
||||
|
||||
for c.ctx.Err() == nil {
|
||||
// 尝试获取锁
|
||||
if !c.mu.TryLock() {
|
||||
continue
|
||||
}
|
||||
|
||||
// 连接池定期清理
|
||||
if time.Since(c.lastClean) >= ReloadInterval {
|
||||
// 发送清理信号到对端
|
||||
if c.ctx.Err() == nil && c.tunnelTCPConn != nil {
|
||||
_, err := c.tunnelTCPConn.Write(c.encode([]byte(c.cleanURL.String())))
|
||||
if err != nil {
|
||||
c.mu.Unlock()
|
||||
return fmt.Errorf("healthCheck: write clean signal failed: %w", err)
|
||||
}
|
||||
}
|
||||
c.tunnelPool.Clean()
|
||||
c.lastClean = time.Now()
|
||||
c.logger.Debug("Tunnel pool cleaned: %v active connections", c.tunnelPool.Active())
|
||||
}
|
||||
|
||||
// 连接池健康度检查
|
||||
if c.tunnelPool.ErrorCount() > c.tunnelPool.Active()/2 {
|
||||
// 发送刷新信号到对端
|
||||
@@ -750,6 +772,57 @@ func (c *Common) healthCheck() error {
|
||||
return fmt.Errorf("healthCheck: context error: %w", c.ctx.Err())
|
||||
}
|
||||
|
||||
// incomingVerify 入口连接验证
|
||||
func (c *Common) incomingVerify() {
|
||||
for c.ctx.Err() == nil {
|
||||
if c.tunnelPool.Ready() {
|
||||
break
|
||||
}
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
continue
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
|
||||
if c.tlsConfig == nil || len(c.tlsConfig.Certificates) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
cert := c.tlsConfig.Certificates[0]
|
||||
if len(cert.Certificate) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// 打印证书指纹
|
||||
c.logger.Info("TLS cert verified: %v", c.formatCertFingerprint(cert.Certificate[0]))
|
||||
|
||||
id, testConn, err := c.tunnelPool.IncomingGet(poolGetTimeout)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer testConn.Close()
|
||||
|
||||
// 构建并发送验证信号
|
||||
verifyURL := &url.URL{
|
||||
Scheme: "np",
|
||||
Host: c.tunnelTCPConn.RemoteAddr().String(),
|
||||
Path: url.PathEscape(id),
|
||||
Fragment: "v", // TLS验证
|
||||
}
|
||||
|
||||
if c.ctx.Err() == nil && c.tunnelTCPConn != nil {
|
||||
c.mu.Lock()
|
||||
_, err = c.tunnelTCPConn.Write(c.encode([]byte(verifyURL.String())))
|
||||
c.mu.Unlock()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.logger.Debug("TLS verify signal: cid %v -> %v", id, c.tunnelTCPConn.RemoteAddr())
|
||||
}
|
||||
|
||||
// commonLoop 共用处理循环
|
||||
func (c *Common) commonLoop() {
|
||||
for c.ctx.Err() == nil {
|
||||
@@ -1029,26 +1102,18 @@ func (c *Common) commonOnce() error {
|
||||
|
||||
// 处理信号
|
||||
switch signalURL.Fragment {
|
||||
case "v": // 验证
|
||||
if c.tlsCode == "1" || c.tlsCode == "2" {
|
||||
go c.outgoingVerify(signalURL)
|
||||
}
|
||||
case "1": // TCP
|
||||
if len(c.targetTCPAddrs) > 0 {
|
||||
if c.disableTCP != "1" {
|
||||
go c.commonTCPOnce(signalURL)
|
||||
}
|
||||
case "2": // UDP
|
||||
if c.disableUDP != "1" {
|
||||
go c.commonUDPOnce(signalURL)
|
||||
}
|
||||
case "c": // 连接池清理
|
||||
go func() {
|
||||
c.tunnelPool.Clean()
|
||||
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
return
|
||||
case <-time.After(reportInterval):
|
||||
}
|
||||
|
||||
c.logger.Debug("Tunnel pool cleaned: %v active connections", c.tunnelPool.Active())
|
||||
}()
|
||||
case "f": // 连接池刷新
|
||||
go func() {
|
||||
c.tunnelPool.Flush()
|
||||
@@ -1087,6 +1152,54 @@ func (c *Common) commonOnce() error {
|
||||
return fmt.Errorf("commonOnce: context error: %w", c.ctx.Err())
|
||||
}
|
||||
|
||||
// outgoingVerify 出口连接验证
|
||||
func (c *Common) outgoingVerify(signalURL *url.URL) {
|
||||
for c.ctx.Err() == nil {
|
||||
if c.tunnelPool.Ready() {
|
||||
break
|
||||
}
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
continue
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
|
||||
id := strings.TrimPrefix(signalURL.Path, "/")
|
||||
if unescapedID, err := url.PathUnescape(id); err != nil {
|
||||
c.logger.Error("outgoingVerify: unescape id failed: %v", err)
|
||||
return
|
||||
} else {
|
||||
id = unescapedID
|
||||
}
|
||||
c.logger.Debug("TLS verify signal: cid %v <- %v", id, c.tunnelTCPConn.RemoteAddr())
|
||||
|
||||
testConn, err := c.tunnelPool.OutgoingGet(id, poolGetTimeout)
|
||||
if err != nil {
|
||||
c.logger.Error("outgoingVerify: request timeout: %v", err)
|
||||
c.tunnelPool.AddError()
|
||||
return
|
||||
}
|
||||
defer testConn.Close()
|
||||
|
||||
if testConn != nil {
|
||||
tlsConn, ok := testConn.(*tls.Conn)
|
||||
if !ok {
|
||||
c.logger.Error("outgoingVerify: connection is not TLS")
|
||||
return
|
||||
}
|
||||
|
||||
state := tlsConn.ConnectionState()
|
||||
if len(state.PeerCertificates) == 0 {
|
||||
c.logger.Error("outgoingVerify: no peer certificates found")
|
||||
return
|
||||
}
|
||||
|
||||
// 打印证书指纹
|
||||
c.logger.Info("TLS cert verified: %v", c.formatCertFingerprint(state.PeerCertificates[0].Raw))
|
||||
}
|
||||
}
|
||||
|
||||
// commonTCPOnce 共用处理单个TCP请求
|
||||
func (c *Common) commonTCPOnce(signalURL *url.URL) {
|
||||
id := strings.TrimPrefix(signalURL.Path, "/")
|
||||
|
||||
@@ -31,14 +31,15 @@ import (
|
||||
|
||||
// 常量定义
|
||||
const (
|
||||
openAPIVersion = "v1" // OpenAPI版本
|
||||
stateFilePath = "gob" // 实例状态持久化文件路径
|
||||
stateFileName = "nodepass.gob" // 实例状态持久化文件名
|
||||
sseRetryTime = 3000 // 重试间隔时间(毫秒)
|
||||
apiKeyID = "********" // API Key的特殊ID
|
||||
tcpingSemLimit = 10 // TCPing最大并发数
|
||||
baseDuration = 100 * time.Millisecond // 基准持续时间
|
||||
maxValueLen = 256 // 字符长度限制
|
||||
openAPIVersion = "v1" // OpenAPI版本
|
||||
stateFilePath = "gob" // 实例状态持久化文件路径
|
||||
stateFileName = "nodepass.gob" // 实例状态持久化文件名
|
||||
sseRetryTime = 3000 // 重试间隔时间(毫秒)
|
||||
apiKeyID = "********" // API Key的特殊ID
|
||||
tcpingSemLimit = 10 // TCPing最大并发数
|
||||
baseDuration = 100 * time.Millisecond // 基准持续时间
|
||||
gracefulTimeout = 5 * time.Second // 优雅关闭超时
|
||||
maxValueLen = 256 // 字符长度限制
|
||||
)
|
||||
|
||||
// Swagger UI HTML模板
|
||||
@@ -1662,30 +1663,39 @@ func (m *Master) stopInstance(instance *Instance) {
|
||||
return
|
||||
}
|
||||
|
||||
// 发送终止信号
|
||||
if instance.cmd.Process != nil {
|
||||
if runtime.GOOS == "windows" {
|
||||
instance.cmd.Process.Signal(os.Interrupt)
|
||||
} else {
|
||||
instance.cmd.Process.Signal(syscall.SIGTERM)
|
||||
}
|
||||
time.Sleep(baseDuration)
|
||||
// 关闭停止通道
|
||||
select {
|
||||
case <-instance.stopped:
|
||||
default:
|
||||
close(instance.stopped)
|
||||
}
|
||||
|
||||
// 关闭停止通道
|
||||
close(instance.stopped)
|
||||
|
||||
// 取消执行或强制终止
|
||||
// 发送终止信号并取消上下文
|
||||
process := instance.cmd.Process
|
||||
if runtime.GOOS == "windows" {
|
||||
process.Signal(os.Interrupt)
|
||||
} else {
|
||||
process.Signal(syscall.SIGTERM)
|
||||
}
|
||||
if instance.cancelFunc != nil {
|
||||
instance.cancelFunc()
|
||||
} else {
|
||||
err := instance.cmd.Process.Kill()
|
||||
if err != nil {
|
||||
m.logger.Error("stopInstance: instance error: %v [%v]", err, instance.ID)
|
||||
}
|
||||
}
|
||||
|
||||
m.logger.Info("Instance stopped [%v]", instance.ID)
|
||||
// 等待优雅退出或超时强制终止
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
process.Wait()
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
m.logger.Info("Instance stopped [%v]", instance.ID)
|
||||
case <-time.After(gracefulTimeout):
|
||||
process.Kill()
|
||||
<-done
|
||||
m.logger.Warn("Instance force killed [%v]", instance.ID)
|
||||
}
|
||||
|
||||
// 重置实例状态
|
||||
instance.Status = "stopped"
|
||||
@@ -1775,7 +1785,7 @@ func (m *Master) generateConfigURL(instance *Instance) string {
|
||||
// 根据实例类型设置默认参数
|
||||
switch instance.Type {
|
||||
case "client":
|
||||
// client参数: min, mode, read, rate, slot, proxy, noudp
|
||||
// client参数: min, mode, read, rate, slot, proxy, notcp, noudp
|
||||
if query.Get("min") == "" {
|
||||
query.Set("min", strconv.Itoa(defaultMinPool))
|
||||
}
|
||||
@@ -1794,11 +1804,14 @@ func (m *Master) generateConfigURL(instance *Instance) string {
|
||||
if query.Get("proxy") == "" {
|
||||
query.Set("proxy", defaultProxyProtocol)
|
||||
}
|
||||
if query.Get("notcp") == "" {
|
||||
query.Set("notcp", defaultTCPStrategy)
|
||||
}
|
||||
if query.Get("noudp") == "" {
|
||||
query.Set("noudp", defaultUDPStrategy)
|
||||
}
|
||||
case "server":
|
||||
// server参数: max, mode, read, rate, slot, proxy, noudp
|
||||
// server参数: max, mode, read, rate, slot, proxy, notcp, noudp
|
||||
if query.Get("max") == "" {
|
||||
query.Set("max", strconv.Itoa(defaultMaxPool))
|
||||
}
|
||||
@@ -1817,6 +1830,9 @@ func (m *Master) generateConfigURL(instance *Instance) string {
|
||||
if query.Get("proxy") == "" {
|
||||
query.Set("proxy", defaultProxyProtocol)
|
||||
}
|
||||
if query.Get("notcp") == "" {
|
||||
query.Set("notcp", defaultTCPStrategy)
|
||||
}
|
||||
if query.Get("noudp") == "" {
|
||||
query.Set("noudp", defaultUDPStrategy)
|
||||
}
|
||||
|
||||
@@ -23,9 +23,8 @@ import (
|
||||
|
||||
// Server 实现服务端模式功能
|
||||
type Server struct {
|
||||
Common // 继承共享功能
|
||||
tlsConfig *tls.Config // TLS配置
|
||||
clientIP string // 客户端IP
|
||||
Common // 继承共享功能
|
||||
clientIP string // 客户端IP
|
||||
}
|
||||
|
||||
// NewServer 创建新的服务端实例
|
||||
@@ -33,6 +32,7 @@ func NewServer(parsedURL *url.URL, tlsCode string, tlsConfig *tls.Config, logger
|
||||
server := &Server{
|
||||
Common: Common{
|
||||
tlsCode: tlsCode,
|
||||
tlsConfig: tlsConfig,
|
||||
logger: logger,
|
||||
signalChan: make(chan string, semaphoreLimit),
|
||||
tcpBufferPool: &sync.Pool{
|
||||
@@ -47,12 +47,10 @@ func NewServer(parsedURL *url.URL, tlsCode string, tlsConfig *tls.Config, logger
|
||||
return &buf
|
||||
},
|
||||
},
|
||||
cleanURL: &url.URL{Scheme: "np", Fragment: "c"},
|
||||
flushURL: &url.URL{Scheme: "np", Fragment: "f"},
|
||||
pingURL: &url.URL{Scheme: "np", Fragment: "i"},
|
||||
pongURL: &url.URL{Scheme: "np", Fragment: "o"},
|
||||
},
|
||||
tlsConfig: tlsConfig,
|
||||
}
|
||||
if err := server.initConfig(parsedURL); err != nil {
|
||||
return nil, fmt.Errorf("newServer: initConfig failed: %w", err)
|
||||
@@ -64,9 +62,9 @@ func NewServer(parsedURL *url.URL, tlsCode string, tlsConfig *tls.Config, logger
|
||||
// Run 管理服务端生命周期
|
||||
func (s *Server) Run() {
|
||||
logInfo := func(prefix string) {
|
||||
s.logger.Info("%v: server://%v@%v/%v?max=%v&mode=%v&read=%v&rate=%v&slot=%v&proxy=%v&noudp=%v",
|
||||
s.logger.Info("%v: server://%v@%v/%v?max=%v&mode=%v&read=%v&rate=%v&slot=%v&proxy=%v¬cp=%v&noudp=%v",
|
||||
prefix, s.tunnelKey, s.tunnelTCPAddr, s.getTargetAddrsString(),
|
||||
s.maxPoolCapacity, s.runMode, s.readTimeout, s.rateLimit/125000, s.slotLimit, s.proxyProtocol, s.disableUDP)
|
||||
s.maxPoolCapacity, s.runMode, s.readTimeout, s.rateLimit/125000, s.slotLimit, s.proxyProtocol, s.disableTCP, s.disableUDP)
|
||||
}
|
||||
logInfo("Server started")
|
||||
|
||||
@@ -152,9 +150,12 @@ func (s *Server) start() error {
|
||||
reportInterval)
|
||||
go s.tunnelPool.ServerManager()
|
||||
|
||||
// 判断数据流向
|
||||
if s.dataFlow == "-" {
|
||||
go s.commonLoop()
|
||||
}
|
||||
|
||||
// 启动共用控制
|
||||
if err := s.commonControl(); err != nil {
|
||||
return fmt.Errorf("start: commonControl failed: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user