feat: 添加IP竞速拨号器与IP偏好设置,连接状态显示域名与实际IP
- 新增竞速拨号器(racedial.go):域名连接时并行解析所有A/AAAA记录, 同时对所有IP发起TCP连接,第一个成功的胜出,确保选择延迟最低的地址 - ServerProfile新增IPPreference字段(auto/v4/v6),支持用户指定IP版本偏好 - 填写服务器IP时跳过域名解析直接使用IP,顺序failover - 连接成功后状态栏显示「已连接 (域名 -> 实际IP)」 - DB迁移v6:server_profiles表添加ip_preference列 - WebSocket拨号与HTTP登录均使用竞速拨号器 - 补充中英文i18n翻译
This commit is contained in:
@@ -18,6 +18,7 @@ import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
@@ -30,13 +31,14 @@ import (
|
||||
|
||||
// HandshakeConfig configures a single connection attempt.
|
||||
type HandshakeConfig struct {
|
||||
ServerURL string // e.g. wss://vpn.example.com/ws
|
||||
SNIHost string // TLS SNI hostname for CDN edge connections
|
||||
Token string // JWT; if non-empty, used via ?token= (method A)
|
||||
Username string // for password auth (method B), or fallback
|
||||
Password string // for password auth (method B), or fallback
|
||||
OnInit func(protocol.InitMessage) error // configure TUN; nil = auto-ready
|
||||
TLSConfig *tls.Config // pre-built TLS config (nil = derive from SNIHost)
|
||||
ServerURL string // e.g. wss://vpn.example.com/ws
|
||||
SNIHost string // TLS SNI hostname for CDN edge connections
|
||||
Token string // JWT; if non-empty, used via ?token= (method A)
|
||||
Username string // for password auth (method B), or fallback
|
||||
Password string // for password auth (method B), or fallback
|
||||
OnInit func(protocol.InitMessage) error // configure TUN; nil = auto-ready
|
||||
TLSConfig *tls.Config // pre-built TLS config (nil = derive from SNIHost)
|
||||
IPPreference string // "auto" (default), "v4", "v6" - hostname mode only
|
||||
}
|
||||
|
||||
// Conn is an established VPN tunnel connection.
|
||||
@@ -53,6 +55,7 @@ type Conn struct {
|
||||
// error occurs.
|
||||
func Connect(ctx context.Context, cfg HandshakeConfig) (*Conn, error) {
|
||||
dialer := websocket.Dialer{
|
||||
NetDialContext: NewRaceDialer(cfg.IPPreference),
|
||||
HandshakeTimeout: 15 * time.Second,
|
||||
ReadBufferSize: 4096, // match server (handler.go:17)
|
||||
WriteBufferSize: 4096, // match server (handler.go:18)
|
||||
@@ -269,6 +272,20 @@ func (c *Conn) AssignedIP() string { return c.init.IP }
|
||||
// AssignedIP6 returns the IPv6 assigned by the server (empty if none).
|
||||
func (c *Conn) AssignedIP6() string { return c.init.IP6 }
|
||||
|
||||
// RemoteIP returns the remote IP address of the underlying TCP
|
||||
// connection (the actual server/CDN IP the WebSocket is connected to).
|
||||
// Returns an empty string if the connection is not established.
|
||||
func (c *Conn) RemoteIP() string {
|
||||
if c.ws == nil {
|
||||
return ""
|
||||
}
|
||||
addr := c.ws.RemoteAddr()
|
||||
if tcpAddr, ok := addr.(*net.TCPAddr); ok {
|
||||
return tcpAddr.IP.String()
|
||||
}
|
||||
return addr.String()
|
||||
}
|
||||
|
||||
// Close terminates the connection.
|
||||
func (c *Conn) Close() error {
|
||||
c.mu.Lock()
|
||||
|
||||
Reference in New Issue
Block a user