diff --git a/README.md b/README.md index 700d486..5e2106d 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,7 @@ To start using the solution, you'll need to configure both server and client com ```toml [client] # Behind NAT, firewall-blocked remote_addr = "0.0.0.0:3080" # Server address and port (mandatory). + edge_ip = "188.114.96.0" # Edge IP used for CDN connection, specifically for WebSocket-based transports.(Optional, default none) transport = "tcp" # Protocol to use ("tcp", "tcpmux", "ws", "wss", "wsmux", "wssmux". mandatory). token = "your_token" # Authentication token for secure communication (optional). connection_pool = 8 # Number of pre-established connections.(optional, default: 8). @@ -202,38 +203,6 @@ To start using the solution, you'll need to configure both server and client com `nodelay`: Refers to a TCP socket option (TCP_NODELAY) that improve the latency but decrease the bandwidth -#### UDP Configuration -* **Server**: - - ```toml - [server] - bind_addr = "0.0.0.0:3080" - transport = "udp" - token = "your_token" - heartbeat = 20 - channel_size = 2048 - sniffer = false - web_port = 2060 - sniffer_log = "/root/backhaul.json" - log_level = "info" - ports = [] - ``` -* **Client**: - - ```toml - [client] - remote_addr = "0.0.0.0:3080" - transport = "udp" - token = "your_token" - connection_pool = 8 - aggressive_pool = false - retry_interval = 3 - sniffer = false - web_port = 2060 - sniffer_log = "/root/backhaul.json" - log_level = "info" - - ``` #### TCP Multiplexing Configuration * **Server**: @@ -287,6 +256,39 @@ To start using the solution, you'll need to configure both server and client com * Refer to TCP configuration for more information. +#### UDP Configuration +* **Server**: + + ```toml + [server] + bind_addr = "0.0.0.0:3080" + transport = "udp" + token = "your_token" + heartbeat = 20 + channel_size = 2048 + sniffer = false + web_port = 2060 + sniffer_log = "/root/backhaul.json" + log_level = "info" + ports = [] + ``` +* **Client**: + + ```toml + [client] + remote_addr = "0.0.0.0:3080" + transport = "udp" + token = "your_token" + connection_pool = 8 + aggressive_pool = false + retry_interval = 3 + sniffer = false + web_port = 2060 + sniffer_log = "/root/backhaul.json" + log_level = "info" + + ``` + #### WebSocket Configuration * **Server**: @@ -311,6 +313,7 @@ To start using the solution, you'll need to configure both server and client com ```toml [client] remote_addr = "0.0.0.0:8080" + edge_ip = "" transport = "ws" token = "your_token" connection_pool = 8 @@ -354,6 +357,7 @@ To start using the solution, you'll need to configure both server and client com ```toml [client] remote_addr = "0.0.0.0:8443" + edge_ip = "" transport = "wss" token = "your_token" connection_pool = 8 @@ -401,6 +405,7 @@ To start using the solution, you'll need to configure both server and client com ```toml [client] remote_addr = "0.0.0.0:3080" + edge_ip = "" transport = "wsmux" token = "your_token" connection_pool = 8 @@ -449,6 +454,7 @@ To start using the solution, you'll need to configure both server and client com ```toml [client] remote_addr = "0.0.0.0:443" + edge_ip = "" transport = "wssmux" token = "your_token" keepalive_period = 75 diff --git a/internal/client/client.go b/internal/client/client.go index 76b6f99..24876f0 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -79,25 +79,26 @@ func (c *Client) Start() { Sniffer: c.config.Sniffer, WebPort: c.config.WebPort, SnifferLog: c.config.SnifferLog, - AggressivePool: c.config.AggressivePool, + AggressivePool: c.config.AggressivePool, } tcpMuxClient := transport.NewMuxClient(c.ctx, tcpMuxConfig, c.logger) go tcpMuxClient.Start() } else if c.config.Transport == config.WS || c.config.Transport == config.WSS { WsConfig := &transport.WsConfig{ - RemoteAddr: c.config.RemoteAddr, - Nodelay: c.config.Nodelay, - KeepAlive: time.Duration(c.config.Keepalive) * time.Second, - RetryInterval: time.Duration(c.config.RetryInterval) * time.Second, - DialTimeOut: time.Duration(c.config.DialTimeout) * time.Second, - ConnPoolSize: c.config.ConnectionPool, - Token: c.config.Token, - Sniffer: c.config.Sniffer, - WebPort: c.config.WebPort, - SnifferLog: c.config.SnifferLog, - Mode: c.config.Transport, + RemoteAddr: c.config.RemoteAddr, + Nodelay: c.config.Nodelay, + KeepAlive: time.Duration(c.config.Keepalive) * time.Second, + RetryInterval: time.Duration(c.config.RetryInterval) * time.Second, + DialTimeOut: time.Duration(c.config.DialTimeout) * time.Second, + ConnPoolSize: c.config.ConnectionPool, + Token: c.config.Token, + Sniffer: c.config.Sniffer, + WebPort: c.config.WebPort, + SnifferLog: c.config.SnifferLog, + Mode: c.config.Transport, AggressivePool: c.config.AggressivePool, + EdgeIP: c.config.EdgeIP, } WsClient := transport.NewWSClient(c.ctx, WsConfig, c.logger) go WsClient.Start() @@ -119,7 +120,8 @@ func (c *Client) Start() { WebPort: c.config.WebPort, SnifferLog: c.config.SnifferLog, Mode: c.config.Transport, - AggressivePool: c.config.AggressivePool, + AggressivePool: c.config.AggressivePool, + EdgeIP: c.config.EdgeIP, } wsMuxClient := transport.NewWSMuxClient(c.ctx, wsMuxConfig, c.logger) go wsMuxClient.Start() @@ -143,14 +145,14 @@ func (c *Client) Start() { } else if c.config.Transport == config.UDP { udpConfig := &transport.UdpConfig{ - RemoteAddr: c.config.RemoteAddr, - RetryInterval: time.Duration(c.config.RetryInterval) * time.Second, - DialTimeOut: time.Duration(c.config.DialTimeout) * time.Second, - ConnPoolSize: c.config.ConnectionPool, - Token: c.config.Token, - Sniffer: c.config.Sniffer, - WebPort: c.config.WebPort, - SnifferLog: c.config.SnifferLog, + RemoteAddr: c.config.RemoteAddr, + RetryInterval: time.Duration(c.config.RetryInterval) * time.Second, + DialTimeOut: time.Duration(c.config.DialTimeout) * time.Second, + ConnPoolSize: c.config.ConnectionPool, + Token: c.config.Token, + Sniffer: c.config.Sniffer, + WebPort: c.config.WebPort, + SnifferLog: c.config.SnifferLog, AggressivePool: c.config.AggressivePool, } udpClient := transport.NewUDPClient(c.ctx, udpConfig, c.logger) diff --git a/internal/client/transport/shared.go b/internal/client/transport/shared.go index 4bb6776..b2b173c 100644 --- a/internal/client/transport/shared.go +++ b/internal/client/transport/shared.go @@ -135,7 +135,7 @@ func ReusePortControl(network, address string, s syscall.RawConn) error { return controlErr } -func WebSocketDialer(ctx context.Context, addr string, path string, timeout time.Duration, keepalive time.Duration, nodelay bool, token string, mode config.TransportType, retry int) (*websocket.Conn, error) { +func WebSocketDialer(ctx context.Context, addr string, edgeIP string, path string, timeout time.Duration, keepalive time.Duration, nodelay bool, token string, mode config.TransportType, retry int) (*websocket.Conn, error) { var tunnelWSConn *websocket.Conn var err error @@ -144,7 +144,7 @@ func WebSocketDialer(ctx context.Context, addr string, path string, timeout time for i := 0; i < retries; i++ { // Attempt to dial the WebSocket - tunnelWSConn, err = attemptDialWebSocket(ctx, addr, path, timeout, keepalive, nodelay, token, mode) + tunnelWSConn, err = attemptDialWebSocket(ctx, addr, edgeIP, path, timeout, keepalive, nodelay, token, mode) if err == nil { // If successful, return the connection return tunnelWSConn, nil @@ -163,7 +163,7 @@ func WebSocketDialer(ctx context.Context, addr string, path string, timeout time return nil, err } -func attemptDialWebSocket(ctx context.Context, addr string, path string, timeout time.Duration, keepalive time.Duration, nodelay bool, token string, mode config.TransportType) (*websocket.Conn, error) { +func attemptDialWebSocket(ctx context.Context, addr string, edgeIP string, path string, timeout time.Duration, keepalive time.Duration, nodelay bool, token string, mode config.TransportType) (*websocket.Conn, error) { // Setup headers with authorization headers := http.Header{} headers.Add("Authorization", fmt.Sprintf("Bearer %v", token)) @@ -171,6 +171,18 @@ func attemptDialWebSocket(ctx context.Context, addr string, path string, timeout var wsURL string dialer := websocket.Dialer{} + // Handle edgeIP assignment + if edgeIP != "" { + _, port, err := net.SplitHostPort(addr) + if err != nil { + return nil, fmt.Errorf("invalid address format, failed to parse: %w", err) + } + + edgeIP = fmt.Sprintf("%s:%s", edgeIP, port) + } else { + edgeIP = addr + } + if mode == config.WS || mode == config.WSMUX { wsURL = fmt.Sprintf("ws://%s%s", addr, path) @@ -178,7 +190,7 @@ func attemptDialWebSocket(ctx context.Context, addr string, path string, timeout EnableCompression: true, HandshakeTimeout: 45 * time.Second, // default handshake timeout NetDial: func(_, addr string) (net.Conn, error) { - conn, err := TcpDialer(ctx, addr, timeout, keepalive, nodelay, 1) + conn, err := TcpDialer(ctx, edgeIP, timeout, keepalive, nodelay, 1) if err != nil { return nil, err } @@ -198,7 +210,7 @@ func attemptDialWebSocket(ctx context.Context, addr string, path string, timeout TLSClientConfig: tlsConfig, // Pass the insecure TLS config here HandshakeTimeout: 45 * time.Second, // default handshake timeout NetDial: func(_, addr string) (net.Conn, error) { - conn, err := TcpDialer(ctx, addr, timeout, keepalive, nodelay, 1) + conn, err := TcpDialer(ctx, edgeIP, timeout, keepalive, nodelay, 1) if err != nil { return nil, err } diff --git a/internal/client/transport/ws.go b/internal/client/transport/ws.go index aef15ca..9fdad40 100644 --- a/internal/client/transport/ws.go +++ b/internal/client/transport/ws.go @@ -43,6 +43,7 @@ type WsConfig struct { WebPort int Mode config.TransportType AggressivePool bool + EdgeIP string } func NewWSClient(parentCtx context.Context, config *WsConfig, logger *logrus.Logger) *WsTransport { @@ -127,7 +128,7 @@ func (c *WsTransport) channelDialer() { case <-c.ctx.Done(): return default: - tunnelWSConn, err := WebSocketDialer(c.ctx, c.config.RemoteAddr, "/channel", c.config.DialTimeOut, c.config.KeepAlive, c.config.Nodelay, c.config.Token, c.config.Mode, 3) + tunnelWSConn, err := WebSocketDialer(c.ctx, c.config.RemoteAddr, c.config.EdgeIP, "/channel", c.config.DialTimeOut, c.config.KeepAlive, c.config.Nodelay, c.config.Token, c.config.Mode, 3) if err != nil { c.logger.Errorf("control channel dialer: %v", err) time.Sleep(c.config.RetryInterval) @@ -284,7 +285,7 @@ func (c *WsTransport) tunnelDialer() { c.logger.Debugf("initiating new websocket tunnel connection to address %s", c.config.RemoteAddr) // Dial to the tunnel server - tunnelConn, err := WebSocketDialer(c.ctx, c.config.RemoteAddr, "/tunnel", c.config.DialTimeOut, c.config.KeepAlive, c.config.Nodelay, c.config.Token, c.config.Mode, 3) + tunnelConn, err := WebSocketDialer(c.ctx, c.config.RemoteAddr, c.config.EdgeIP, "/tunnel", c.config.DialTimeOut, c.config.KeepAlive, c.config.Nodelay, c.config.Token, c.config.Mode, 3) if err != nil { c.logger.Errorf("tunnel server dialer: %v", err) diff --git a/internal/client/transport/wsmux.go b/internal/client/transport/wsmux.go index aa7484d..1d59114 100644 --- a/internal/client/transport/wsmux.go +++ b/internal/client/transport/wsmux.go @@ -48,6 +48,7 @@ type WsMuxConfig struct { WebPort int Mode config.TransportType AggressivePool bool + EdgeIP string } func NewWSMuxClient(parentCtx context.Context, config *WsMuxConfig, logger *logrus.Logger) *WsMuxTransport { @@ -140,7 +141,7 @@ func (c *WsMuxTransport) channelDialer() { return default: - tunnelWSConn, err := WebSocketDialer(c.ctx, c.config.RemoteAddr, "/channel", c.config.DialTimeOut, c.config.KeepAlive, c.config.Nodelay, c.config.Token, c.config.Mode, 3) + tunnelWSConn, err := WebSocketDialer(c.ctx, c.config.RemoteAddr, c.config.EdgeIP, "/channel", c.config.DialTimeOut, c.config.KeepAlive, c.config.Nodelay, c.config.Token, c.config.Mode, 3) if err != nil { c.logger.Errorf("control channel dialer: %v", err) time.Sleep(c.config.RetryInterval) @@ -295,7 +296,7 @@ func (c *WsMuxTransport) tunnelDialer() { c.logger.Debugf("initiating new %s tunnel connection to address %s", c.config.Mode, c.config.RemoteAddr) // Dial to the tunnel server - tunnelWSConn, err := WebSocketDialer(c.ctx, c.config.RemoteAddr, "/tunnel", c.config.DialTimeOut, c.config.KeepAlive, c.config.Nodelay, c.config.Token, c.config.Mode, 3) + tunnelWSConn, err := WebSocketDialer(c.ctx, c.config.RemoteAddr, c.config.EdgeIP, "/tunnel", c.config.DialTimeOut, c.config.KeepAlive, c.config.Nodelay, c.config.Token, c.config.Mode, 3) if err != nil { c.logger.Errorf("tunnel server dialer: %v", err) diff --git a/internal/config/config.go b/internal/config/config.go index ca68cb6..c6ac28b 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -61,6 +61,7 @@ type ClientConfig struct { SnifferLog string `toml:"sniffer_log"` DialTimeout int `toml:"dial_timeout"` AggressivePool bool `toml:"aggressive_pool"` + EdgeIP string `toml:"edge_ip"` } // Config represents the complete configuration, including both server and client settings.