From 51f2d6b33f7f531cde5dec625f001c94fc616d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 9 Nov 2023 16:59:44 +0800 Subject: [PATCH 01/11] Migrate to gobwas/ws --- common/dialer/detour.go | 10 ++- experimental/clashapi/api_meta.go | 17 ++-- experimental/clashapi/connections.go | 9 +- experimental/clashapi/server.go | 38 ++++----- go.mod | 4 +- go.sum | 9 +- transport/v2ray/transport.go | 2 +- transport/v2rayhttp/client.go | 2 +- transport/v2raywebsocket/client.go | 94 +++++++++++---------- transport/v2raywebsocket/conn.go | 120 +++++++++++++++++---------- transport/v2raywebsocket/mask.go | 6 -- transport/v2raywebsocket/server.go | 13 +-- transport/v2raywebsocket/writer.go | 27 ++---- 13 files changed, 191 insertions(+), 160 deletions(-) delete mode 100644 transport/v2raywebsocket/mask.go diff --git a/common/dialer/detour.go b/common/dialer/detour.go index 81600913d4..ff484da29e 100644 --- a/common/dialer/detour.go +++ b/common/dialer/detour.go @@ -6,6 +6,7 @@ import ( "sync" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing/common/bufio/deadline" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" @@ -44,7 +45,14 @@ func (d *DetourDialer) DialContext(ctx context.Context, network string, destinat if err != nil { return nil, err } - return dialer.DialContext(ctx, network, destination) + conn, err := dialer.DialContext(ctx, network, destination) + if err != nil { + return nil, err + } + if deadline.NeedAdditionalReadDeadline(conn) { + conn = deadline.NewConn(conn) + } + return conn, nil } func (d *DetourDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { diff --git a/experimental/clashapi/api_meta.go b/experimental/clashapi/api_meta.go index bfdee1b85b..876f98695d 100644 --- a/experimental/clashapi/api_meta.go +++ b/experimental/clashapi/api_meta.go @@ -2,12 +2,14 @@ package clashapi import ( "bytes" + "net" "net/http" "time" "github.com/sagernet/sing-box/common/json" "github.com/sagernet/sing-box/experimental/clashapi/trafficontrol" - "github.com/sagernet/websocket" + "github.com/sagernet/ws" + "github.com/sagernet/ws/wsutil" "github.com/go-chi/chi/v5" "github.com/go-chi/render" @@ -27,16 +29,16 @@ type Memory struct { func memory(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { - var wsConn *websocket.Conn - if websocket.IsWebSocketUpgrade(r) { + var conn net.Conn + if r.Header.Get("Upgrade") == "websocket" { var err error - wsConn, err = upgrader.Upgrade(w, r, nil) + conn, _, _, err = ws.UpgradeHTTP(r, w) if err != nil { return } } - if wsConn == nil { + if conn == nil { w.Header().Set("Content-Type", "application/json") render.Status(r, http.StatusOK) } @@ -63,13 +65,12 @@ func memory(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r }); err != nil { break } - if wsConn == nil { + if conn == nil { _, err = w.Write(buf.Bytes()) w.(http.Flusher).Flush() } else { - err = wsConn.WriteMessage(websocket.TextMessage, buf.Bytes()) + err = wsutil.WriteServerText(conn, buf.Bytes()) } - if err != nil { break } diff --git a/experimental/clashapi/connections.go b/experimental/clashapi/connections.go index 94cfb9a37a..042bdd3645 100644 --- a/experimental/clashapi/connections.go +++ b/experimental/clashapi/connections.go @@ -9,7 +9,8 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/json" "github.com/sagernet/sing-box/experimental/clashapi/trafficontrol" - "github.com/sagernet/websocket" + "github.com/sagernet/ws" + "github.com/sagernet/ws/wsutil" "github.com/go-chi/chi/v5" "github.com/go-chi/render" @@ -25,13 +26,13 @@ func connectionRouter(router adapter.Router, trafficManager *trafficontrol.Manag func getConnections(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { - if !websocket.IsWebSocketUpgrade(r) { + if r.Header.Get("Upgrade") != "websocket" { snapshot := trafficManager.Snapshot() render.JSON(w, r, snapshot) return } - conn, err := upgrader.Upgrade(w, r, nil) + conn, _, _, err := ws.UpgradeHTTP(r, w) if err != nil { return } @@ -56,7 +57,7 @@ func getConnections(trafficManager *trafficontrol.Manager) func(w http.ResponseW if err := json.NewEncoder(buf).Encode(snapshot); err != nil { return err } - return conn.WriteMessage(websocket.TextMessage, buf.Bytes()) + return wsutil.WriteServerText(conn, buf.Bytes()) } if err = sendSnapshot(); err != nil { diff --git a/experimental/clashapi/server.go b/experimental/clashapi/server.go index 9ac06620d9..493d73972b 100644 --- a/experimental/clashapi/server.go +++ b/experimental/clashapi/server.go @@ -25,7 +25,8 @@ import ( N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/service" "github.com/sagernet/sing/service/filemanager" - "github.com/sagernet/websocket" + "github.com/sagernet/ws" + "github.com/sagernet/ws/wsutil" "github.com/go-chi/chi/v5" "github.com/go-chi/cors" @@ -314,7 +315,7 @@ func authentication(serverSecret string) func(next http.Handler) http.Handler { } // Browser websocket not support custom header - if websocket.IsWebSocketUpgrade(r) && r.URL.Query().Get("token") != "" { + if r.Header.Get("Upgrade") == "websocket" && r.URL.Query().Get("token") != "" { token := r.URL.Query().Get("token") if token != serverSecret { render.Status(r, http.StatusUnauthorized) @@ -351,12 +352,6 @@ func hello(redirect bool) func(w http.ResponseWriter, r *http.Request) { } } -var upgrader = websocket.Upgrader{ - CheckOrigin: func(r *http.Request) bool { - return true - }, -} - type Traffic struct { Up int64 `json:"up"` Down int64 `json:"down"` @@ -364,16 +359,17 @@ type Traffic struct { func traffic(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { - var wsConn *websocket.Conn - if websocket.IsWebSocketUpgrade(r) { + var conn net.Conn + if r.Header.Get("Upgrade") == "websocket" { var err error - wsConn, err = upgrader.Upgrade(w, r, nil) + conn, _, _, err = ws.UpgradeHTTP(r, w) if err != nil { return } + defer conn.Close() } - if wsConn == nil { + if conn == nil { w.Header().Set("Content-Type", "application/json") render.Status(r, http.StatusOK) } @@ -392,11 +388,11 @@ func traffic(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, break } - if wsConn == nil { + if conn == nil { _, err = w.Write(buf.Bytes()) w.(http.Flusher).Flush() } else { - err = wsConn.WriteMessage(websocket.TextMessage, buf.Bytes()) + err = wsutil.WriteServerText(conn, buf.Bytes()) } if err != nil { @@ -432,16 +428,16 @@ func getLogs(logFactory log.ObservableFactory) func(w http.ResponseWriter, r *ht } defer logFactory.UnSubscribe(subscription) - var wsConn *websocket.Conn - if websocket.IsWebSocketUpgrade(r) { - var err error - wsConn, err = upgrader.Upgrade(w, r, nil) + var conn net.Conn + if r.Header.Get("Upgrade") == "websocket" { + conn, _, _, err = ws.UpgradeHTTP(r, w) if err != nil { return } + defer conn.Close() } - if wsConn == nil { + if conn == nil { w.Header().Set("Content-Type", "application/json") render.Status(r, http.StatusOK) } @@ -465,11 +461,11 @@ func getLogs(logFactory log.ObservableFactory) func(w http.ResponseWriter, r *ht if err != nil { break } - if wsConn == nil { + if conn == nil { _, err = w.Write(buf.Bytes()) w.(http.Flusher).Flush() } else { - err = wsConn.WriteMessage(websocket.TextMessage, buf.Bytes()) + err = wsutil.WriteServerText(conn, buf.Bytes()) } if err != nil { diff --git a/go.mod b/go.mod index dd5000ece4..b29772949c 100644 --- a/go.mod +++ b/go.mod @@ -38,8 +38,8 @@ require ( github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 - github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f + github.com/sagernet/ws v0.0.0-20231030053741-7d481eb31bed github.com/spf13/cobra v1.8.0 github.com/stretchr/testify v1.8.4 go.uber.org/zap v1.26.0 @@ -61,6 +61,8 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/gobwas/httphead v0.1.0 // indirect + github.com/gobwas/pool v0.2.1 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect diff --git a/go.sum b/go.sum index d973e70b0e..f536e6c6f6 100644 --- a/go.sum +++ b/go.sum @@ -31,6 +31,10 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M= github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -134,10 +138,10 @@ github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 h1:Px+hN4Vzgx+iCGV github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6/go.mod h1:zovq6vTvEM6ECiqE3Eeb9rpIylPpamPcmrJ9tv0Bt0M= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM= -github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs= -github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY= github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f h1:Kvo8w8Y9lzFGB/7z09MJ3TR99TFtfI/IuY87Ygcycho= github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f/go.mod h1:mySs0abhpc/gLlvhoq7HP1RzOaRmIXVeZGCh++zoApk= +github.com/sagernet/ws v0.0.0-20231030053741-7d481eb31bed h1:90a510OeE9siSJoYsI8nSjPmA+u5ROMDts/ZkdNsuXY= +github.com/sagernet/ws v0.0.0-20231030053741-7d481eb31bed/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA= github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg= github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= @@ -189,6 +193,7 @@ golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/transport/v2ray/transport.go b/transport/v2ray/transport.go index 3481c852c6..9dfee28119 100644 --- a/transport/v2ray/transport.go +++ b/transport/v2ray/transport.go @@ -50,7 +50,7 @@ func NewClientTransport(ctx context.Context, dialer N.Dialer, serverAddr M.Socks case C.V2RayTransportTypeGRPC: return NewGRPCClient(ctx, dialer, serverAddr, options.GRPCOptions, tlsConfig) case C.V2RayTransportTypeWebsocket: - return v2raywebsocket.NewClient(ctx, dialer, serverAddr, options.WebsocketOptions, tlsConfig), nil + return v2raywebsocket.NewClient(ctx, dialer, serverAddr, options.WebsocketOptions, tlsConfig) case C.V2RayTransportTypeQUIC: if tlsConfig == nil { return nil, C.ErrTLSRequired diff --git a/transport/v2rayhttp/client.go b/transport/v2rayhttp/client.go index d5e8a1f670..f280eeef55 100644 --- a/transport/v2rayhttp/client.go +++ b/transport/v2rayhttp/client.go @@ -81,7 +81,7 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt uri.Path = options.Path err := sHTTP.URLSetPath(&uri, options.Path) if err != nil { - return nil, E.New("failed to set path: " + err.Error()) + return nil, E.Cause(err, "parse path") } client.url = &uri return client, nil diff --git a/transport/v2raywebsocket/client.go b/transport/v2raywebsocket/client.go index 9f14f676e6..54c4df0b6e 100644 --- a/transport/v2raywebsocket/client.go +++ b/transport/v2raywebsocket/client.go @@ -5,58 +5,37 @@ import ( "net" "net/http" "net/url" + "strings" "time" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/tls" + C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/option" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" sHTTP "github.com/sagernet/sing/protocol/http" - "github.com/sagernet/websocket" + "github.com/sagernet/ws" ) var _ adapter.V2RayClientTransport = (*Client)(nil) type Client struct { - dialer *websocket.Dialer + dialer N.Dialer + tlsConfig tls.Config + serverAddr M.Socksaddr requestURL url.URL - requestURLString string headers http.Header maxEarlyData uint32 earlyDataHeaderName string } -func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayWebsocketOptions, tlsConfig tls.Config) adapter.V2RayClientTransport { - wsDialer := &websocket.Dialer{ - ReadBufferSize: 4 * 1024, - WriteBufferSize: 4 * 1024, - HandshakeTimeout: time.Second * 8, - } +func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayWebsocketOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) { if tlsConfig != nil { if len(tlsConfig.NextProtos()) == 0 { tlsConfig.SetNextProtos([]string{"http/1.1"}) } - wsDialer.NetDialTLSContext = func(ctx context.Context, network, addr string) (net.Conn, error) { - conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr)) - if err != nil { - return nil, err - } - tlsConn, err := tls.ClientHandshake(ctx, conn, tlsConfig) - if err != nil { - return nil, err - } - return &deadConn{tlsConn}, nil - } - } else { - wsDialer.NetDialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { - conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr)) - if err != nil { - return nil, err - } - return &deadConn{conn}, nil - } } var requestURL url.URL if tlsConfig == nil { @@ -68,37 +47,68 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt requestURL.Path = options.Path err := sHTTP.URLSetPath(&requestURL, options.Path) if err != nil { - return nil + return nil, E.Cause(err, "parse path") + } + if !strings.HasPrefix(requestURL.Path, "/") { + requestURL.Path = "/" + requestURL.Path } headers := make(http.Header) for key, value := range options.Headers { headers[key] = value + if key == "Host" { + if len(value) > 1 { + return nil, E.New("multiple Host headers") + } + requestURL.Host = value[0] + } + } + if headers.Get("User-Agent") == "" { + headers.Set("User-Agent", "Go-http-client/1.1") } return &Client{ - wsDialer, + dialer, + tlsConfig, + serverAddr, requestURL, - requestURL.String(), headers, options.MaxEarlyData, options.EarlyDataHeaderName, + }, nil +} + +func (c *Client) dialContext(ctx context.Context, requestURL *url.URL, headers http.Header) (*WebsocketConn, error) { + conn, err := c.dialer.DialContext(ctx, N.NetworkTCP, c.serverAddr) + if err != nil { + return nil, err + } + if c.tlsConfig != nil { + conn, err = tls.ClientHandshake(ctx, conn, c.tlsConfig) + if err != nil { + return nil, err + } + } + conn.SetDeadline(time.Now().Add(C.TCPTimeout)) + var protocols []string + if protocolHeader := headers.Get("Sec-WebSocket-Protocol"); protocolHeader != "" { + protocols = []string{protocolHeader} + headers.Del("Sec-WebSocket-Protocol") } + reader, _, err := ws.Dialer{Header: ws.HandshakeHeaderHTTP(headers), Protocols: protocols}.Upgrade(conn, requestURL) + conn.SetDeadline(time.Time{}) + if err != nil { + return nil, err + } + return NewConn(conn, reader, nil, ws.StateClientSide), nil } func (c *Client) DialContext(ctx context.Context) (net.Conn, error) { if c.maxEarlyData <= 0 { - conn, response, err := c.dialer.DialContext(ctx, c.requestURLString, c.headers) - if err == nil { - return &WebsocketConn{Conn: conn, Writer: NewWriter(conn, false)}, nil + conn, err := c.dialContext(ctx, &c.requestURL, c.headers) + if err != nil { + return nil, err } - return nil, wrapDialError(response, err) + return conn, nil } else { return &EarlyWebsocketConn{Client: c, ctx: ctx, create: make(chan struct{})}, nil } } - -func wrapDialError(response *http.Response, err error) error { - if response == nil { - return err - } - return E.Extend(err, "HTTP ", response.StatusCode, " ", response.Status) -} diff --git a/transport/v2raywebsocket/conn.go b/transport/v2raywebsocket/conn.go index 6400b118d9..5b51e5c528 100644 --- a/transport/v2raywebsocket/conn.go +++ b/transport/v2raywebsocket/conn.go @@ -1,11 +1,11 @@ package v2raywebsocket import ( + "bufio" "context" "encoding/base64" "io" "net" - "net/http" "os" "sync" "time" @@ -13,50 +13,96 @@ import ( C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" + "github.com/sagernet/sing/common/debug" E "github.com/sagernet/sing/common/exceptions" - "github.com/sagernet/websocket" + "github.com/sagernet/ws" + "github.com/sagernet/ws/wsutil" ) type WebsocketConn struct { - *websocket.Conn + net.Conn *Writer - remoteAddr net.Addr - reader io.Reader + state ws.State + reader *wsutil.Reader + controlHandler wsutil.FrameHandlerFunc + remoteAddr net.Addr } -func NewServerConn(wsConn *websocket.Conn, remoteAddr net.Addr) *WebsocketConn { +func NewConn(conn net.Conn, br *bufio.Reader, remoteAddr net.Addr, state ws.State) *WebsocketConn { + controlHandler := wsutil.ControlFrameHandler(conn, state) + var reader io.Reader + if br != nil && br.Buffered() > 0 { + reader = br + } else { + reader = conn + } return &WebsocketConn{ - Conn: wsConn, - remoteAddr: remoteAddr, - Writer: NewWriter(wsConn, true), + Conn: conn, + state: state, + reader: &wsutil.Reader{ + Source: reader, + State: state, + SkipHeaderCheck: !debug.Enabled, + OnIntermediate: controlHandler, + }, + controlHandler: controlHandler, + remoteAddr: remoteAddr, + Writer: NewWriter(conn, state), } } func (c *WebsocketConn) Close() error { - err := c.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""), time.Now().Add(C.TCPTimeout)) - if err != nil { - return c.Conn.Close() + c.Conn.SetWriteDeadline(time.Now().Add(C.TCPTimeout)) + frame := ws.NewCloseFrame(ws.NewCloseFrameBody( + ws.StatusNormalClosure, "", + )) + if c.state == ws.StateClientSide { + frame = ws.MaskFrameInPlace(frame) } + ws.WriteFrame(c.Conn, frame) + c.Conn.Close() return nil } func (c *WebsocketConn) Read(b []byte) (n int, err error) { + var header ws.Header for { - if c.reader == nil { - _, c.reader, err = c.NextReader() + n, err = c.reader.Read(b) + if n > 0 { + err = nil + return + } + if !E.IsMulti(err, io.EOF, wsutil.ErrNoFrameAdvance) { + return + } + header, err = c.reader.NextFrame() + if err != nil { + return + } + if header.OpCode.IsControl() { + err = c.controlHandler(header, c.reader) if err != nil { - err = wrapError(err) return } + continue } - n, err = c.reader.Read(b) - if E.IsMulti(err, io.EOF) { - c.reader = nil + if header.OpCode&ws.OpBinary == 0 { + err = c.reader.Discard() + if err != nil { + return + } continue } - err = wrapError(err) + } +} + +func (c *WebsocketConn) Write(p []byte) (n int, err error) { + err = wsutil.WriteMessage(c.Conn, c.state, ws.OpBinary, p) + if err != nil { return } + n = len(p) + return } func (c *WebsocketConn) RemoteAddr() net.Addr { @@ -83,11 +129,7 @@ func (c *WebsocketConn) NeedAdditionalReadDeadline() bool { } func (c *WebsocketConn) Upstream() any { - return c.Conn.NetConn() -} - -func (c *WebsocketConn) UpstreamWriter() any { - return c.Writer + return c.Conn } type EarlyWebsocketConn struct { @@ -113,8 +155,7 @@ func (c *EarlyWebsocketConn) writeRequest(content []byte) error { var ( earlyData []byte lateData []byte - conn *websocket.Conn - response *http.Response + conn *WebsocketConn err error ) if len(content) > int(c.maxEarlyData) { @@ -128,23 +169,26 @@ func (c *EarlyWebsocketConn) writeRequest(content []byte) error { if c.earlyDataHeaderName == "" { requestURL := c.requestURL requestURL.Path += earlyDataString - conn, response, err = c.dialer.DialContext(c.ctx, requestURL.String(), c.headers) + conn, err = c.dialContext(c.ctx, &requestURL, c.headers) } else { headers := c.headers.Clone() headers.Set(c.earlyDataHeaderName, earlyDataString) - conn, response, err = c.dialer.DialContext(c.ctx, c.requestURLString, headers) + conn, err = c.dialContext(c.ctx, &c.requestURL, headers) } } else { - conn, response, err = c.dialer.DialContext(c.ctx, c.requestURLString, c.headers) + conn, err = c.dialContext(c.ctx, &c.requestURL, c.headers) } if err != nil { - return wrapDialError(response, err) + return err } - c.conn = &WebsocketConn{Conn: conn, Writer: NewWriter(conn, false)} if len(lateData) > 0 { - _, err = c.conn.Write(lateData) + _, err = conn.Write(lateData) + if err != nil { + return err + } } - return err + c.conn = conn + return nil } func (c *EarlyWebsocketConn) Write(b []byte) (n int, err error) { @@ -230,13 +274,3 @@ func (c *EarlyWebsocketConn) Upstream() any { func (c *EarlyWebsocketConn) LazyHeadroom() bool { return c.conn == nil } - -func wrapError(err error) error { - if websocket.IsCloseError(err, websocket.CloseNormalClosure) { - return io.EOF - } - if websocket.IsCloseError(err, websocket.CloseAbnormalClosure) { - return net.ErrClosed - } - return err -} diff --git a/transport/v2raywebsocket/mask.go b/transport/v2raywebsocket/mask.go deleted file mode 100644 index 01ea8437a1..0000000000 --- a/transport/v2raywebsocket/mask.go +++ /dev/null @@ -1,6 +0,0 @@ -package v2raywebsocket - -import _ "unsafe" - -//go:linkname maskBytes github.com/sagernet/websocket.maskBytes -func maskBytes(key [4]byte, pos int, b []byte) int diff --git a/transport/v2raywebsocket/server.go b/transport/v2raywebsocket/server.go index 9d8bc69af7..ae6e15f376 100644 --- a/transport/v2raywebsocket/server.go +++ b/transport/v2raywebsocket/server.go @@ -20,7 +20,7 @@ import ( N "github.com/sagernet/sing/common/network" aTLS "github.com/sagernet/sing/common/tls" sHttp "github.com/sagernet/sing/protocol/http" - "github.com/sagernet/websocket" + "github.com/sagernet/ws" ) var _ adapter.V2RayServerTransport = (*Server)(nil) @@ -58,13 +58,6 @@ func NewServer(ctx context.Context, options option.V2RayWebsocketOptions, tlsCon return server, nil } -var upgrader = websocket.Upgrader{ - HandshakeTimeout: C.TCPTimeout, - CheckOrigin: func(r *http.Request) bool { - return true - }, -} - func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) { if s.maxEarlyData == 0 || s.earlyDataHeaderName != "" { if request.URL.Path != s.path { @@ -95,14 +88,14 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) { s.invalidRequest(writer, request, http.StatusBadRequest, E.Cause(err, "decode early data")) return } - wsConn, err := upgrader.Upgrade(writer, request, nil) + wsConn, reader, _, err := ws.UpgradeHTTP(request, writer) if err != nil { s.invalidRequest(writer, request, 0, E.Cause(err, "upgrade websocket connection")) return } var metadata M.Metadata metadata.Source = sHttp.SourceAddress(request) - conn = NewServerConn(wsConn, metadata.Source.TCPAddr()) + conn = NewConn(wsConn, reader.Reader, metadata.Source.TCPAddr(), ws.StateServerSide) if len(earlyData) > 0 { conn = bufio.NewCachedConn(conn, buf.As(earlyData)) } diff --git a/transport/v2raywebsocket/writer.go b/transport/v2raywebsocket/writer.go index fbb61f0f4d..5bd0d0a14f 100644 --- a/transport/v2raywebsocket/writer.go +++ b/transport/v2raywebsocket/writer.go @@ -2,36 +2,27 @@ package v2raywebsocket import ( "encoding/binary" + "io" "math/rand" "github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/bufio" N "github.com/sagernet/sing/common/network" - "github.com/sagernet/websocket" + "github.com/sagernet/ws" ) type Writer struct { - *websocket.Conn writer N.ExtendedWriter isServer bool } -func NewWriter(conn *websocket.Conn, isServer bool) *Writer { +func NewWriter(writer io.Writer, state ws.State) *Writer { return &Writer{ - conn, - bufio.NewExtendedWriter(conn.NetConn()), - isServer, + bufio.NewExtendedWriter(writer), + state == ws.StateServerSide, } } -func (w *Writer) Write(p []byte) (n int, err error) { - err = w.Conn.WriteMessage(websocket.BinaryMessage, p) - if err != nil { - return - } - return len(p), nil -} - func (w *Writer) WriteBuffer(buffer *buf.Buffer) error { var payloadBitLength int dataLen := buffer.Len() @@ -52,7 +43,7 @@ func (w *Writer) WriteBuffer(buffer *buf.Buffer) error { } header := buffer.ExtendHeader(headerLen) - header[0] = websocket.BinaryMessage | 1<<7 + header[0] = byte(ws.OpBinary) | 0x80 if w.isServer { header[1] = 0 } else { @@ -72,16 +63,12 @@ func (w *Writer) WriteBuffer(buffer *buf.Buffer) error { if !w.isServer { maskKey := rand.Uint32() binary.BigEndian.PutUint32(header[1+payloadBitLength:], maskKey) - maskBytes(*(*[4]byte)(header[1+payloadBitLength:]), 0, data) + ws.Cipher(data, *(*[4]byte)(header[1+payloadBitLength:]), 0) } return w.writer.WriteBuffer(buffer) } -func (w *Writer) Upstream() any { - return w.Conn.NetConn() -} - func (w *Writer) FrontHeadroom() int { return 14 } From f48ddf99bee77690bc191fd9014952b9c795d28d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Mon, 30 Oct 2023 12:00:00 +0800 Subject: [PATCH 02/11] Add udp_disable_domain_unmapping inbound listen option --- docs/configuration/shared/listen.md | 54 +++++++++++++------------- docs/configuration/shared/listen.zh.md | 38 ++++++++---------- go.mod | 2 +- go.sum | 4 +- option/inbound.go | 9 +++-- outbound/default.go | 10 +++-- 6 files changed, 58 insertions(+), 59 deletions(-) diff --git a/docs/configuration/shared/listen.md b/docs/configuration/shared/listen.md index d4a0e58ee3..c1b1ed3382 100644 --- a/docs/configuration/shared/listen.md +++ b/docs/configuration/shared/listen.md @@ -7,28 +7,26 @@ "tcp_fast_open": false, "tcp_multi_path": false, "udp_fragment": false, + "udp_timeout": 300, + "detour": "another-in", "sniff": false, "sniff_override_destination": false, "sniff_timeout": "300ms", "domain_strategy": "prefer_ipv6", - "udp_timeout": 300, - "proxy_protocol": false, - "proxy_protocol_accept_no_header": false, - "detour": "another-in" + "udp_disable_domain_unmapping": false } ``` ### Fields -| Field | Available Context | -|-----------------------------------|-------------------------------------------------------------------| -| `listen` | Needs to listen on TCP or UDP. | -| `listen_port` | Needs to listen on TCP or UDP. | -| `tcp_fast_open` | Needs to listen on TCP. | -| `tcp_multi_path` | Needs to listen on TCP. | -| `udp_timeout` | Needs to assemble UDP connections, currently Tun and Shadowsocks. | -| `proxy_protocol` | Needs to listen on TCP. | -| `proxy_protocol_accept_no_header` | When `proxy_protocol` enabled | +| Field | Available Context | +|--------------------------------|-------------------------------------------------------------------| +| `listen` | Needs to listen on TCP or UDP. | +| `listen_port` | Needs to listen on TCP or UDP. | +| `tcp_fast_open` | Needs to listen on TCP. | +| `tcp_multi_path` | Needs to listen on TCP. | +| `udp_timeout` | Needs to assemble UDP connections, currently Tun and Shadowsocks. | +| `udp_disable_domain_unmapping` | Needs to listen on UDP and accept domain UDP addresses. | #### listen @@ -56,6 +54,16 @@ Enable TCP Multi Path. Enable UDP fragmentation. +#### udp_timeout + +UDP NAT expiration time in seconds, default is 300 (5 minutes). + +#### detour + +If set, connections will be forwarded to the specified inbound. + +Requires target inbound support, see [Injectable](/configuration/inbound/#fields). + #### sniff Enable sniffing. @@ -82,20 +90,10 @@ If set, the requested domain name will be resolved to IP before routing. If `sniff_override_destination` is in effect, its value will be taken as a fallback. -#### udp_timeout - -UDP NAT expiration time in seconds, default is 300 (5 minutes). - -#### proxy_protocol - -Parse [Proxy Protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) in the connection header. +#### udp_disable_domain_unmapping -#### proxy_protocol_accept_no_header - -Accept connections without Proxy Protocol header. - -#### detour - -If set, connections will be forwarded to the specified inbound. +If enabled, for UDP proxy requests addressed to a domain, +the original packet address will be sent in the response instead of the mapped domain. -Requires target inbound support, see [Injectable](/configuration/inbound/#fields). \ No newline at end of file +This option is used for compatibility with clients that +do not support receiving UDP packets with domain addresses, such as Surge. diff --git a/docs/configuration/shared/listen.zh.md b/docs/configuration/shared/listen.zh.md index b25ce295c0..b7fd74870a 100644 --- a/docs/configuration/shared/listen.zh.md +++ b/docs/configuration/shared/listen.zh.md @@ -7,14 +7,13 @@ "tcp_fast_open": false, "tcp_multi_path": false, "udp_fragment": false, + "udp_timeout": 300, + "detour": "another-in", "sniff": false, "sniff_override_destination": false, "sniff_timeout": "300ms", "domain_strategy": "prefer_ipv6", - "udp_timeout": 300, - "proxy_protocol": false, - "proxy_protocol_accept_no_header": false, - "detour": "another-in" + "udp_disable_domain_unmapping": false } ``` @@ -26,8 +25,7 @@ | `tcp_fast_open` | 需要监听 TCP。 | | `tcp_multi_path` | 需要监听 TCP。 | | `udp_timeout` | 需要组装 UDP 连接, 当前为 Tun 和 Shadowsocks。 | -| `proxy_protocol` | 需要监听 TCP。 | -| `proxy_protocol_accept_no_header` | `proxy_protocol` 启用时 | +| ### 字段 @@ -57,6 +55,16 @@ 启用 UDP 分段。 +#### udp_timeout + +UDP NAT 过期时间,以秒为单位,默认为 300(5 分钟)。 + +#### detour + +如果设置,连接将被转发到指定的入站。 + +需要目标入站支持,参阅 [注入支持](/zh/configuration/inbound/#_3)。 + #### sniff 启用协议探测。 @@ -83,20 +91,8 @@ 如果 `sniff_override_destination` 生效,它的值将作为后备。 -#### udp_timeout - -UDP NAT 过期时间,以秒为单位,默认为 300(5 分钟)。 - -#### proxy_protocol - -解析连接头中的 [代理协议](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)。 +#### udp_disable_domain_unmapping -#### proxy_protocol_accept_no_header - -接受没有代理协议标头的连接。 - -#### detour - -如果设置,连接将被转发到指定的入站。 +如果启用,对于地址为域的 UDP 代理请求,将在响应中发送原始包地址而不是映射的域。 -需要目标入站支持,参阅 [注入支持](/zh/configuration/inbound/#_3)。 \ No newline at end of file +此选项用于兼容不支持接收带有域地址的 UDP 包的客户端,如 Surge。 diff --git a/go.mod b/go.mod index b29772949c..4b3ec2a711 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab github.com/sagernet/quic-go v0.0.0-20231008035953-32727fef9460 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 - github.com/sagernet/sing v0.2.17 + github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358 github.com/sagernet/sing-mux v0.1.4 github.com/sagernet/sing-quic v0.1.4-0.20231111111624-370e6abf6769 diff --git a/go.sum b/go.sum index f536e6c6f6..fa0b5230b8 100644 --- a/go.sum +++ b/go.sum @@ -114,8 +114,8 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= -github.com/sagernet/sing v0.2.17 h1:vMPKb3MV0Aa5ws4dCJkRI8XEjrsUcDn810czd0FwmzI= -github.com/sagernet/sing v0.2.17/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= +github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c h1:uask61Pxc3nGqsOSjqnBKrwfODWRoEa80lXm04LNk0E= +github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358 h1:psJQg/KXVQTupFFR1liaCAucW+NwCDhe1oYfkmz8EJ8= github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358/go.mod h1:vtUimtf7Nq9EdvD5WTpfCr69KL1M7bcgOVKiYBiAY/c= github.com/sagernet/sing-mux v0.1.4 h1:BPNPOQr6HkXG3iY/BrfvUKUl+A7gYsGKVSxvoR3PO50= diff --git a/option/inbound.go b/option/inbound.go index 06408f58b2..c61428adb5 100644 --- a/option/inbound.go +++ b/option/inbound.go @@ -120,10 +120,11 @@ func (h *Inbound) UnmarshalJSON(bytes []byte) error { } type InboundOptions struct { - SniffEnabled bool `json:"sniff,omitempty"` - SniffOverrideDestination bool `json:"sniff_override_destination,omitempty"` - SniffTimeout Duration `json:"sniff_timeout,omitempty"` - DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"` + SniffEnabled bool `json:"sniff,omitempty"` + SniffOverrideDestination bool `json:"sniff_override_destination,omitempty"` + SniffTimeout Duration `json:"sniff_timeout,omitempty"` + DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"` + UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"` } type ListenOptions struct { diff --git a/outbound/default.go b/outbound/default.go index 8067b6db5d..972aca9479 100644 --- a/outbound/default.go +++ b/outbound/default.go @@ -124,9 +124,13 @@ func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, } if destinationAddress.IsValid() { if metadata.Destination.IsFqdn() { - outConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination) + if metadata.InboundOptions.UDPDisableDomainUnmapping { + outConn = bufio.NewUnidirectionalNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination) + } else { + outConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination) + } } - if natConn, loaded := common.Cast[*bufio.NATPacketConn](conn); loaded { + if natConn, loaded := common.Cast[bufio.NATPacketConn](conn); loaded { natConn.UpdateDestination(destinationAddress) } } @@ -170,7 +174,7 @@ func NewDirectPacketConnection(ctx context.Context, router adapter.Router, this if metadata.Destination.IsFqdn() { outConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination) } - if natConn, loaded := common.Cast[*bufio.NATPacketConn](conn); loaded { + if natConn, loaded := common.Cast[bufio.NATPacketConn](conn); loaded { natConn.UpdateDestination(destinationAddress) } } From 1e02ea4aa1bcd87379496231b9a6c8ca145543fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Mon, 30 Oct 2023 12:00:00 +0800 Subject: [PATCH 03/11] Add exclude route support for tun --- docs/configuration/inbound/tun.md | 14 +++++++++ docs/configuration/inbound/tun.zh.md | 14 +++++++++ experimental/libbox/service.go | 6 +++- experimental/libbox/tun.go | 9 ++++-- go.mod | 2 +- go.sum | 4 +-- inbound/tun.go | 36 ++++++++++++----------- option/tun.go | 44 +++++++++++++++------------- 8 files changed, 85 insertions(+), 44 deletions(-) diff --git a/docs/configuration/inbound/tun.md b/docs/configuration/inbound/tun.md index 4c9670a27d..e6c52c54c1 100644 --- a/docs/configuration/inbound/tun.md +++ b/docs/configuration/inbound/tun.md @@ -22,6 +22,12 @@ "::/1", "8000::/1" ], + "inet4_route_exclude_address": [ + "192.168.0.0/16" + ], + "inet6_route_exclude_address": [ + "fc00::/7" + ], "endpoint_independent_nat": false, "stack": "system", "include_interface": [ @@ -130,6 +136,14 @@ Use custom routes instead of default when `auto_route` is enabled. Use custom routes instead of default when `auto_route` is enabled. +#### inet4_route_exclude_address + +Exclude custom routes when `auto_route` is enabled. + +#### inet6_route_exclude_address + +Exclude custom routes when `auto_route` is enabled. + #### endpoint_independent_nat !!! info "" diff --git a/docs/configuration/inbound/tun.zh.md b/docs/configuration/inbound/tun.zh.md index fbd10abf7e..8f246c042b 100644 --- a/docs/configuration/inbound/tun.zh.md +++ b/docs/configuration/inbound/tun.zh.md @@ -22,6 +22,12 @@ "::/1", "8000::/1" ], + "inet4_route_exclude_address": [ + "192.168.0.0/16" + ], + "inet6_route_exclude_address": [ + "fc00::/7" + ], "endpoint_independent_nat": false, "stack": "system", "include_interface": [ @@ -131,6 +137,14 @@ tun 接口的 IPv6 前缀。 启用 `auto_route` 时使用自定义路由而不是默认路由。 +#### inet4_route_exclude_address + +启用 `auto_route` 时排除自定义路由。 + +#### inet6_route_exclude_address + +启用 `auto_route` 时排除自定义路由。 + #### endpoint_independent_nat 启用独立于端点的 NAT。 diff --git a/experimental/libbox/service.go b/experimental/libbox/service.go index 67b57d7da5..ce4d6d2aad 100644 --- a/experimental/libbox/service.go +++ b/experimental/libbox/service.go @@ -122,7 +122,11 @@ func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions if len(options.IncludeAndroidUser) > 0 { return nil, E.New("android: unsupported android_user option") } - tunFd, err := w.iif.OpenTun(&tunOptions{options, platformOptions}) + routeRanges, err := options.BuildAutoRouteRanges(true) + if err != nil { + return nil, err + } + tunFd, err := w.iif.OpenTun(&tunOptions{options, routeRanges, platformOptions}) if err != nil { return nil, err } diff --git a/experimental/libbox/tun.go b/experimental/libbox/tun.go index e692a5d651..e40ad58b46 100644 --- a/experimental/libbox/tun.go +++ b/experimental/libbox/tun.go @@ -60,6 +60,7 @@ var _ TunOptions = (*tunOptions)(nil) type tunOptions struct { *tun.Options + routeRanges []netip.Prefix option.TunPlatformOptions } @@ -91,11 +92,15 @@ func (o *tunOptions) GetStrictRoute() bool { } func (o *tunOptions) GetInet4RouteAddress() RoutePrefixIterator { - return mapRoutePrefix(o.Inet4RouteAddress) + return mapRoutePrefix(common.Filter(o.routeRanges, func(it netip.Prefix) bool { + return it.Addr().Is4() + })) } func (o *tunOptions) GetInet6RouteAddress() RoutePrefixIterator { - return mapRoutePrefix(o.Inet6RouteAddress) + return mapRoutePrefix(common.Filter(o.routeRanges, func(it netip.Prefix) bool { + return it.Addr().Is6() + })) } func (o *tunOptions) GetIncludePackage() StringIterator { diff --git a/go.mod b/go.mod index 4b3ec2a711..e5a3ea30ca 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/sagernet/sing-shadowsocks v0.2.5 github.com/sagernet/sing-shadowsocks2 v0.1.4 github.com/sagernet/sing-shadowtls v0.1.4 - github.com/sagernet/sing-tun v0.1.20-0.20231116102114-958d6a25a41d + github.com/sagernet/sing-tun v0.1.20-0.20231116102736-3fa4ee409a9d github.com/sagernet/sing-vmess v0.1.8 github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 diff --git a/go.sum b/go.sum index fa0b5230b8..e2872ed0ff 100644 --- a/go.sum +++ b/go.sum @@ -128,8 +128,8 @@ github.com/sagernet/sing-shadowsocks2 v0.1.4 h1:vht2M8t3m5DTgXR2j24KbYOygG5aOp+M github.com/sagernet/sing-shadowsocks2 v0.1.4/go.mod h1:Mgdee99NxxNd5Zld3ixIs18yVs4x2dI2VTDDE1N14Wc= github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= -github.com/sagernet/sing-tun v0.1.20-0.20231116102114-958d6a25a41d h1:c0M01hMieuYwOMk+xzgudn0jNjPLUR15I/QnXk0eLkc= -github.com/sagernet/sing-tun v0.1.20-0.20231116102114-958d6a25a41d/go.mod h1:6kkPL/u9tWcLFfu55VbwMDnO++17cUihSmImkZjdZro= +github.com/sagernet/sing-tun v0.1.20-0.20231116102736-3fa4ee409a9d h1:FlCho4lZEMC6N6dThTVd2rOkWPW0iTqgy7YEAboeyps= +github.com/sagernet/sing-tun v0.1.20-0.20231116102736-3fa4ee409a9d/go.mod h1:B1y93eY/4pNklxrstVGFrDrH4dUZAiZFBQ4MFn6HJXA= github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc= github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= diff --git a/inbound/tun.go b/inbound/tun.go index 0b57482d39..7d1f519934 100644 --- a/inbound/tun.go +++ b/inbound/tun.go @@ -71,23 +71,25 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger logger: logger, inboundOptions: options.InboundOptions, tunOptions: tun.Options{ - Name: options.InterfaceName, - MTU: tunMTU, - Inet4Address: options.Inet4Address, - Inet6Address: options.Inet6Address, - AutoRoute: options.AutoRoute, - StrictRoute: options.StrictRoute, - IncludeInterface: options.IncludeInterface, - ExcludeInterface: options.ExcludeInterface, - Inet4RouteAddress: options.Inet4RouteAddress, - Inet6RouteAddress: options.Inet6RouteAddress, - IncludeUID: includeUID, - ExcludeUID: excludeUID, - IncludeAndroidUser: options.IncludeAndroidUser, - IncludePackage: options.IncludePackage, - ExcludePackage: options.ExcludePackage, - InterfaceMonitor: router.InterfaceMonitor(), - TableIndex: 2022, + Name: options.InterfaceName, + MTU: tunMTU, + Inet4Address: options.Inet4Address, + Inet6Address: options.Inet6Address, + AutoRoute: options.AutoRoute, + StrictRoute: options.StrictRoute, + IncludeInterface: options.IncludeInterface, + ExcludeInterface: options.ExcludeInterface, + Inet4RouteAddress: options.Inet4RouteAddress, + Inet6RouteAddress: options.Inet6RouteAddress, + Inet4RouteExcludeAddress: options.Inet4RouteExcludeAddress, + Inet6RouteExcludeAddress: options.Inet6RouteExcludeAddress, + IncludeUID: includeUID, + ExcludeUID: excludeUID, + IncludeAndroidUser: options.IncludeAndroidUser, + IncludePackage: options.IncludePackage, + ExcludePackage: options.ExcludePackage, + InterfaceMonitor: router.InterfaceMonitor(), + TableIndex: 2022, }, endpointIndependentNat: options.EndpointIndependentNat, udpTimeout: udpTimeout, diff --git a/option/tun.go b/option/tun.go index 4cf778041b..306d45415d 100644 --- a/option/tun.go +++ b/option/tun.go @@ -3,26 +3,28 @@ package option import "net/netip" type TunInboundOptions struct { - InterfaceName string `json:"interface_name,omitempty"` - MTU uint32 `json:"mtu,omitempty"` - Inet4Address Listable[netip.Prefix] `json:"inet4_address,omitempty"` - Inet6Address Listable[netip.Prefix] `json:"inet6_address,omitempty"` - AutoRoute bool `json:"auto_route,omitempty"` - StrictRoute bool `json:"strict_route,omitempty"` - Inet4RouteAddress Listable[netip.Prefix] `json:"inet4_route_address,omitempty"` - Inet6RouteAddress Listable[netip.Prefix] `json:"inet6_route_address,omitempty"` - IncludeInterface Listable[string] `json:"include_interface,omitempty"` - ExcludeInterface Listable[string] `json:"exclude_interface,omitempty"` - IncludeUID Listable[uint32] `json:"include_uid,omitempty"` - IncludeUIDRange Listable[string] `json:"include_uid_range,omitempty"` - ExcludeUID Listable[uint32] `json:"exclude_uid,omitempty"` - ExcludeUIDRange Listable[string] `json:"exclude_uid_range,omitempty"` - IncludeAndroidUser Listable[int] `json:"include_android_user,omitempty"` - IncludePackage Listable[string] `json:"include_package,omitempty"` - ExcludePackage Listable[string] `json:"exclude_package,omitempty"` - EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"` - UDPTimeout int64 `json:"udp_timeout,omitempty"` - Stack string `json:"stack,omitempty"` - Platform *TunPlatformOptions `json:"platform,omitempty"` + InterfaceName string `json:"interface_name,omitempty"` + MTU uint32 `json:"mtu,omitempty"` + Inet4Address Listable[netip.Prefix] `json:"inet4_address,omitempty"` + Inet6Address Listable[netip.Prefix] `json:"inet6_address,omitempty"` + AutoRoute bool `json:"auto_route,omitempty"` + StrictRoute bool `json:"strict_route,omitempty"` + Inet4RouteAddress Listable[netip.Prefix] `json:"inet4_route_address,omitempty"` + Inet6RouteAddress Listable[netip.Prefix] `json:"inet6_route_address,omitempty"` + Inet4RouteExcludeAddress Listable[netip.Prefix] `json:"inet4_route_exclude_address,omitempty"` + Inet6RouteExcludeAddress Listable[netip.Prefix] `json:"inet6_route_exclude_address,omitempty"` + IncludeInterface Listable[string] `json:"include_interface,omitempty"` + ExcludeInterface Listable[string] `json:"exclude_interface,omitempty"` + IncludeUID Listable[uint32] `json:"include_uid,omitempty"` + IncludeUIDRange Listable[string] `json:"include_uid_range,omitempty"` + ExcludeUID Listable[uint32] `json:"exclude_uid,omitempty"` + ExcludeUIDRange Listable[string] `json:"exclude_uid_range,omitempty"` + IncludeAndroidUser Listable[int] `json:"include_android_user,omitempty"` + IncludePackage Listable[string] `json:"include_package,omitempty"` + ExcludePackage Listable[string] `json:"exclude_package,omitempty"` + EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"` + UDPTimeout int64 `json:"udp_timeout,omitempty"` + Stack string `json:"stack,omitempty"` + Platform *TunPlatformOptions `json:"platform,omitempty"` InboundOptions } From 9d5206f677535998bb4d8bae287cda7f550a3834 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 3 Nov 2023 01:47:25 +0800 Subject: [PATCH 04/11] Add support for v2ray http upgrade transport --- constant/v2ray.go | 9 +- docs/configuration/shared/v2ray-transport.md | 30 ++++ .../shared/v2ray-transport.zh.md | 30 ++++ option/v2ray_transport.go | 21 ++- test/go.mod | 22 +-- test/go.sum | 47 +++--- test/v2ray_httpupgrade_test.go | 16 ++ transport/v2ray/transport.go | 6 +- transport/v2raygrpclite/client.go | 2 +- transport/v2raygrpclite/server.go | 8 +- transport/v2rayhttp/client.go | 2 +- transport/v2rayhttp/server.go | 8 +- transport/v2rayhttpupgrade/client.go | 118 +++++++++++++++ transport/v2rayhttpupgrade/server.go | 139 ++++++++++++++++++ 14 files changed, 407 insertions(+), 51 deletions(-) create mode 100644 test/v2ray_httpupgrade_test.go create mode 100644 transport/v2rayhttpupgrade/client.go create mode 100644 transport/v2rayhttpupgrade/server.go diff --git a/constant/v2ray.go b/constant/v2ray.go index 2243d736fb..c3089a6cf6 100644 --- a/constant/v2ray.go +++ b/constant/v2ray.go @@ -1,8 +1,9 @@ package constant const ( - V2RayTransportTypeHTTP = "http" - V2RayTransportTypeWebsocket = "ws" - V2RayTransportTypeQUIC = "quic" - V2RayTransportTypeGRPC = "grpc" + V2RayTransportTypeHTTP = "http" + V2RayTransportTypeWebsocket = "ws" + V2RayTransportTypeQUIC = "quic" + V2RayTransportTypeGRPC = "grpc" + V2RayTransportTypeHTTPUpgrade = "httpupgrade" ) diff --git a/docs/configuration/shared/v2ray-transport.md b/docs/configuration/shared/v2ray-transport.md index 4b5b6f6669..418ef28db2 100644 --- a/docs/configuration/shared/v2ray-transport.md +++ b/docs/configuration/shared/v2ray-transport.md @@ -15,6 +15,7 @@ Available transports: * WebSocket * QUIC * gRPC +* HTTPUpgrade !!! warning "Difference from v2ray-core" @@ -184,3 +185,32 @@ In standard gRPC client: If enabled, the client transport sends keepalive pings even with no active connections. If disabled, when there are no active connections, `idle_timeout` and `ping_timeout` will be ignored and no keepalive pings will be sent. Disabled by default. + +### HTTPUpgrade + +```json +{ + "type": "httpupgrade", + "host": "", + "path": "", + "headers": {} +} +``` + +#### host + +Host domain. + +The server will verify if not empty. + +#### path + +Path of HTTP request. + +The server will verify if not empty. + +#### headers + +Extra headers of HTTP request. + +The server will write in response if not empty. diff --git a/docs/configuration/shared/v2ray-transport.zh.md b/docs/configuration/shared/v2ray-transport.zh.md index deab558983..2ea93562d6 100644 --- a/docs/configuration/shared/v2ray-transport.zh.md +++ b/docs/configuration/shared/v2ray-transport.zh.md @@ -14,6 +14,7 @@ V2Ray Transport 是 v2ray 发明的一组私有协议,并污染了其他协议 * WebSocket * QUIC * gRPC +* HTTPUpgrade !!! warning "与 v2ray-core 的区别" @@ -183,3 +184,32 @@ gRPC 服务名称。 如果启用,客户端传输即使没有活动连接也会发送 keepalive ping。如果禁用,则在没有活动连接时,将忽略 `idle_timeout` 和 `ping_timeout`,并且不会发送 keepalive ping。 默认禁用。 + +### HTTPUpgrade + +```json +{ + "type": "httpupgrade", + "host": "", + "path": "", + "headers": {} +} +``` + +#### host + +主机域名。 + +默认服务器将验证。 + +#### path + +HTTP 请求路径 + +默认服务器将验证。 + +#### headers + +HTTP 请求的额外标头。 + +默认服务器将写入响应。 diff --git a/option/v2ray_transport.go b/option/v2ray_transport.go index 54b0de7913..63af28a371 100644 --- a/option/v2ray_transport.go +++ b/option/v2ray_transport.go @@ -7,11 +7,12 @@ import ( ) type _V2RayTransportOptions struct { - Type string `json:"type,omitempty"` - HTTPOptions V2RayHTTPOptions `json:"-"` - WebsocketOptions V2RayWebsocketOptions `json:"-"` - QUICOptions V2RayQUICOptions `json:"-"` - GRPCOptions V2RayGRPCOptions `json:"-"` + Type string `json:"type,omitempty"` + HTTPOptions V2RayHTTPOptions `json:"-"` + WebsocketOptions V2RayWebsocketOptions `json:"-"` + QUICOptions V2RayQUICOptions `json:"-"` + GRPCOptions V2RayGRPCOptions `json:"-"` + HTTPUpgradeOptions V2RayHTTPUpgradeOptions `json:"-"` } type V2RayTransportOptions _V2RayTransportOptions @@ -29,6 +30,8 @@ func (o V2RayTransportOptions) MarshalJSON() ([]byte, error) { v = o.QUICOptions case C.V2RayTransportTypeGRPC: v = o.GRPCOptions + case C.V2RayTransportTypeHTTPUpgrade: + v = o.HTTPUpgradeOptions default: return nil, E.New("unknown transport type: " + o.Type) } @@ -50,6 +53,8 @@ func (o *V2RayTransportOptions) UnmarshalJSON(bytes []byte) error { v = &o.QUICOptions case C.V2RayTransportTypeGRPC: v = &o.GRPCOptions + case C.V2RayTransportTypeHTTPUpgrade: + v = &o.HTTPUpgradeOptions default: return E.New("unknown transport type: " + o.Type) } @@ -85,3 +90,9 @@ type V2RayGRPCOptions struct { PermitWithoutStream bool `json:"permit_without_stream,omitempty"` ForceLite bool `json:"-"` // for test } + +type V2RayHTTPUpgradeOptions struct { + Host string `json:"host,omitempty"` + Path string `json:"path,omitempty"` + Headers HTTPHeader `json:"headers,omitempty"` +} diff --git a/test/go.mod b/test/go.mod index 856f10f157..108a09608b 100644 --- a/test/go.mod +++ b/test/go.mod @@ -11,15 +11,15 @@ require ( github.com/docker/go-connections v0.4.0 github.com/gofrs/uuid/v5 v5.0.0 github.com/sagernet/quic-go v0.0.0-20231008035953-32727fef9460 - github.com/sagernet/sing v0.2.16-0.20231021090846-8002db54c028 - github.com/sagernet/sing-dns v0.1.10 - github.com/sagernet/sing-quic v0.1.3-0.20231026034240-fa3d997246b6 + github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c + github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358 + github.com/sagernet/sing-quic v0.1.4-0.20231111111624-370e6abf6769 github.com/sagernet/sing-shadowsocks v0.2.5 github.com/sagernet/sing-shadowsocks2 v0.1.4 github.com/spyzhov/ajson v0.9.0 github.com/stretchr/testify v1.8.4 go.uber.org/goleak v1.3.0 - golang.org/x/net v0.17.0 + golang.org/x/net v0.18.0 ) require ( @@ -40,6 +40,8 @@ require ( github.com/go-chi/render v1.0.3 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/gobwas/httphead v0.1.0 // indirect + github.com/gobwas/pool v0.2.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/btree v1.1.2 // indirect @@ -73,15 +75,15 @@ require ( github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect - github.com/sagernet/sing-mux v0.1.3 // indirect + github.com/sagernet/sing-mux v0.1.4 // indirect github.com/sagernet/sing-shadowtls v0.1.4 // indirect - github.com/sagernet/sing-tun v0.1.17-0.20231026060825-efd9884154a6 // indirect + github.com/sagernet/sing-tun v0.1.20-0.20231116102736-3fa4ee409a9d // indirect github.com/sagernet/sing-vmess v0.1.8 // indirect github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 // indirect github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 // indirect - github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f // indirect + github.com/sagernet/ws v0.0.0-20231030053741-7d481eb31bed // indirect github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect @@ -89,11 +91,11 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect go4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect - golang.org/x/crypto v0.14.0 // indirect + golang.org/x/crypto v0.15.0 // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/mod v0.13.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.14.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect diff --git a/test/go.sum b/test/go.sum index bd89d8b67e..b4a07c6cb8 100644 --- a/test/go.sum +++ b/test/go.sum @@ -43,6 +43,10 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M= github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -127,22 +131,22 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= -github.com/sagernet/sing v0.2.16-0.20231021090846-8002db54c028 h1:6GbQt7SC9y5Imrq5jDMbXDSaNiMhJ8KBjhjtQRuqQvE= -github.com/sagernet/sing v0.2.16-0.20231021090846-8002db54c028/go.mod h1:AhNEHu0GXrpqkuzvTwvC8+j2cQUU/dh+zLEmq4C99pg= -github.com/sagernet/sing-dns v0.1.10 h1:iIU7nRBlUYj+fF2TaktGIvRiTFFrHwSMedLQsvlTZCI= -github.com/sagernet/sing-dns v0.1.10/go.mod h1:vtUimtf7Nq9EdvD5WTpfCr69KL1M7bcgOVKiYBiAY/c= -github.com/sagernet/sing-mux v0.1.3 h1:fAf7PZa2A55mCeh0KKM02f1k2Y4vEmxuZZ/51ahkkLA= -github.com/sagernet/sing-mux v0.1.3/go.mod h1:wGeIeiiFLx4HUM5LAg65wrNZ/X1muOimqK0PEhNbPi0= -github.com/sagernet/sing-quic v0.1.3-0.20231026034240-fa3d997246b6 h1:w+TUbIZKZFSdf/AUa/y33kY9xaLeNGz/tBNcNhqpqfg= -github.com/sagernet/sing-quic v0.1.3-0.20231026034240-fa3d997246b6/go.mod h1:1M7xP4802K9Kz6BQ7LlA7UeCapWvWlH1Htmk2bAqkWc= +github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c h1:uask61Pxc3nGqsOSjqnBKrwfODWRoEa80lXm04LNk0E= +github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= +github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358 h1:psJQg/KXVQTupFFR1liaCAucW+NwCDhe1oYfkmz8EJ8= +github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358/go.mod h1:vtUimtf7Nq9EdvD5WTpfCr69KL1M7bcgOVKiYBiAY/c= +github.com/sagernet/sing-mux v0.1.4 h1:BPNPOQr6HkXG3iY/BrfvUKUl+A7gYsGKVSxvoR3PO50= +github.com/sagernet/sing-mux v0.1.4/go.mod h1:dKvcu/sb3fZ88uGv9vzAqUej6J4W+pHu5GqjRuFwAWs= +github.com/sagernet/sing-quic v0.1.4-0.20231111111624-370e6abf6769 h1:V84NPJKirL/oDkvUh9Dor2gMZ+TTNHK3jcedqRkgcQA= +github.com/sagernet/sing-quic v0.1.4-0.20231111111624-370e6abf6769/go.mod h1:iggBfFE2ojS2yYQU8Hnrkm029RsHPdYYIKWqeCI64HE= github.com/sagernet/sing-shadowsocks v0.2.5 h1:qxIttos4xu6ii7MTVJYA8EFQR7Q3KG6xMqmLJIFtBaY= github.com/sagernet/sing-shadowsocks v0.2.5/go.mod h1:MGWGkcU2xW2G2mfArT9/QqpVLOGU+dBaahZCtPHdt7A= github.com/sagernet/sing-shadowsocks2 v0.1.4 h1:vht2M8t3m5DTgXR2j24KbYOygG5aOp+MUhpQnAux728= github.com/sagernet/sing-shadowsocks2 v0.1.4/go.mod h1:Mgdee99NxxNd5Zld3ixIs18yVs4x2dI2VTDDE1N14Wc= github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= -github.com/sagernet/sing-tun v0.1.17-0.20231026060825-efd9884154a6 h1:4yEXBqQoUgXj7qPSLD6lr+z9/KfsvixO9JUA2i5xnM8= -github.com/sagernet/sing-tun v0.1.17-0.20231026060825-efd9884154a6/go.mod h1:w2+S+uWE94E/pQWSDdDdMIjwAEb645kuGPunr6ZllUg= +github.com/sagernet/sing-tun v0.1.20-0.20231116102736-3fa4ee409a9d h1:FlCho4lZEMC6N6dThTVd2rOkWPW0iTqgy7YEAboeyps= +github.com/sagernet/sing-tun v0.1.20-0.20231116102736-3fa4ee409a9d/go.mod h1:B1y93eY/4pNklxrstVGFrDrH4dUZAiZFBQ4MFn6HJXA= github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc= github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= @@ -151,10 +155,10 @@ github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 h1:Px+hN4Vzgx+iCGV github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6/go.mod h1:zovq6vTvEM6ECiqE3Eeb9rpIylPpamPcmrJ9tv0Bt0M= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM= -github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs= -github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY= github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f h1:Kvo8w8Y9lzFGB/7z09MJ3TR99TFtfI/IuY87Ygcycho= github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f/go.mod h1:mySs0abhpc/gLlvhoq7HP1RzOaRmIXVeZGCh++zoApk= +github.com/sagernet/ws v0.0.0-20231030053741-7d481eb31bed h1:90a510OeE9siSJoYsI8nSjPmA+u5ROMDts/ZkdNsuXY= +github.com/sagernet/ws v0.0.0-20231030053741-7d481eb31bed/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA= github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg= github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s= github.com/spyzhov/ajson v0.9.0 h1:tF46gJGOenYVj+k9K1U1XpCxVWhmiyY5PsVCAs1+OJ0= @@ -190,8 +194,8 @@ golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaE golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -204,8 +208,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -222,15 +226,16 @@ golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/test/v2ray_httpupgrade_test.go b/test/v2ray_httpupgrade_test.go new file mode 100644 index 0000000000..9a79aa3af9 --- /dev/null +++ b/test/v2ray_httpupgrade_test.go @@ -0,0 +1,16 @@ +package main + +import ( + "testing" + + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/option" +) + +func TestV2RayHTTPUpgrade(t *testing.T) { + t.Run("self", func(t *testing.T) { + testV2RayTransportSelf(t, &option.V2RayTransportOptions{ + Type: C.V2RayTransportTypeHTTPUpgrade, + }) + }) +} diff --git a/transport/v2ray/transport.go b/transport/v2ray/transport.go index 9dfee28119..deb8a7f0ee 100644 --- a/transport/v2ray/transport.go +++ b/transport/v2ray/transport.go @@ -8,6 +8,7 @@ import ( C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/transport/v2rayhttp" + "github.com/sagernet/sing-box/transport/v2rayhttpupgrade" "github.com/sagernet/sing-box/transport/v2raywebsocket" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" @@ -35,6 +36,8 @@ func NewServerTransport(ctx context.Context, options option.V2RayTransportOption return NewQUICServer(ctx, options.QUICOptions, tlsConfig, handler) case C.V2RayTransportTypeGRPC: return NewGRPCServer(ctx, options.GRPCOptions, tlsConfig, handler) + case C.V2RayTransportTypeHTTPUpgrade: + return v2rayhttpupgrade.NewServer(ctx, options.HTTPUpgradeOptions, tlsConfig, handler) default: return nil, E.New("unknown transport type: " + options.Type) } @@ -56,7 +59,8 @@ func NewClientTransport(ctx context.Context, dialer N.Dialer, serverAddr M.Socks return nil, C.ErrTLSRequired } return NewQUICClient(ctx, dialer, serverAddr, options.QUICOptions, tlsConfig) - + case C.V2RayTransportTypeHTTPUpgrade: + return v2rayhttpupgrade.NewClient(ctx, dialer, serverAddr, options.HTTPUpgradeOptions, tlsConfig) default: return nil, E.New("unknown transport type: " + options.Type) } diff --git a/transport/v2raygrpclite/client.go b/transport/v2raygrpclite/client.go index 8480ac533b..588a813385 100644 --- a/transport/v2raygrpclite/client.go +++ b/transport/v2raygrpclite/client.go @@ -100,7 +100,7 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) { conn.setup(nil, err) } else if response.StatusCode != 200 { response.Body.Close() - conn.setup(nil, E.New("unexpected status: ", response.StatusCode, " ", response.Status)) + conn.setup(nil, E.New("unexpected status: ", response.Status)) } else { conn.setup(response.Body, nil) } diff --git a/transport/v2raygrpclite/server.go b/transport/v2raygrpclite/server.go index a3025ca6e7..6d3e42ebe5 100644 --- a/transport/v2raygrpclite/server.go +++ b/transport/v2raygrpclite/server.go @@ -35,10 +35,6 @@ type Server struct { path string } -func (s *Server) Network() []string { - return []string{N.NetworkTCP} -} - func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (*Server, error) { server := &Server{ tlsConfig: tlsConfig, @@ -92,6 +88,10 @@ func (s *Server) invalidRequest(writer http.ResponseWriter, request *http.Reques s.handler.NewError(request.Context(), E.Cause(err, "process connection from ", request.RemoteAddr)) } +func (s *Server) Network() []string { + return []string{N.NetworkTCP} +} + func (s *Server) Serve(listener net.Listener) error { if s.tlsConfig != nil { if !common.Contains(s.tlsConfig.NextProtos(), http2.NextProtoTLS) { diff --git a/transport/v2rayhttp/client.go b/transport/v2rayhttp/client.go index f280eeef55..44c135ef6f 100644 --- a/transport/v2rayhttp/client.go +++ b/transport/v2rayhttp/client.go @@ -143,7 +143,7 @@ func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) { conn.Setup(nil, err) } else if response.StatusCode != 200 { response.Body.Close() - conn.Setup(nil, E.New("unexpected status: ", response.StatusCode, " ", response.Status)) + conn.Setup(nil, E.New("unexpected status: ", response.Status)) } else { conn.Setup(response.Body, nil) } diff --git a/transport/v2rayhttp/server.go b/transport/v2rayhttp/server.go index dcfb07a6e5..ef5fffcd2d 100644 --- a/transport/v2rayhttp/server.go +++ b/transport/v2rayhttp/server.go @@ -40,10 +40,6 @@ type Server struct { headers http.Header } -func (s *Server) Network() []string { - return []string{N.NetworkTCP} -} - func NewServer(ctx context.Context, options option.V2RayHTTPOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (*Server, error) { server := &Server{ ctx: ctx, @@ -153,6 +149,10 @@ func (s *Server) invalidRequest(writer http.ResponseWriter, request *http.Reques s.handler.NewError(request.Context(), E.Cause(err, "process connection from ", request.RemoteAddr)) } +func (s *Server) Network() []string { + return []string{N.NetworkTCP} +} + func (s *Server) Serve(listener net.Listener) error { if s.tlsConfig != nil { if len(s.tlsConfig.NextProtos()) == 0 { diff --git a/transport/v2rayhttpupgrade/client.go b/transport/v2rayhttpupgrade/client.go new file mode 100644 index 0000000000..c10e1b8fdc --- /dev/null +++ b/transport/v2rayhttpupgrade/client.go @@ -0,0 +1,118 @@ +package v2rayhttpupgrade + +import ( + std_bufio "bufio" + "context" + "net" + "net/http" + "net/url" + "strings" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/tls" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing/common/buf" + "github.com/sagernet/sing/common/bufio" + E "github.com/sagernet/sing/common/exceptions" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + sHTTP "github.com/sagernet/sing/protocol/http" +) + +var _ adapter.V2RayClientTransport = (*Client)(nil) + +type Client struct { + dialer N.Dialer + tlsConfig tls.Config + serverAddr M.Socksaddr + requestURL url.URL + headers http.Header + host string +} + +func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayHTTPUpgradeOptions, tlsConfig tls.Config) (*Client, error) { + if tlsConfig != nil { + if len(tlsConfig.NextProtos()) == 0 { + tlsConfig.SetNextProtos([]string{"http/1.1"}) + } + } + var host string + if options.Host != "" { + host = options.Host + } else if tlsConfig != nil && tlsConfig.ServerName() != "" { + host = tlsConfig.ServerName() + } else { + host = serverAddr.String() + } + var requestURL url.URL + if tlsConfig == nil { + requestURL.Scheme = "http" + } else { + requestURL.Scheme = "https" + } + requestURL.Host = serverAddr.String() + requestURL.Path = options.Path + err := sHTTP.URLSetPath(&requestURL, options.Path) + if err != nil { + return nil, E.Cause(err, "parse path") + } + if !strings.HasPrefix(requestURL.Path, "/") { + requestURL.Path = "/" + requestURL.Path + } + headers := make(http.Header) + for key, value := range options.Headers { + headers[key] = value + } + return &Client{ + dialer: dialer, + tlsConfig: tlsConfig, + serverAddr: serverAddr, + requestURL: requestURL, + headers: headers, + host: host, + }, nil +} + +func (c *Client) DialContext(ctx context.Context) (net.Conn, error) { + conn, err := c.dialer.DialContext(ctx, N.NetworkTCP, c.serverAddr) + if err != nil { + return nil, err + } + if c.tlsConfig != nil { + conn, err = tls.ClientHandshake(ctx, conn, c.tlsConfig) + if err != nil { + return nil, err + } + } + request := &http.Request{ + Method: http.MethodGet, + URL: &c.requestURL, + Header: c.headers.Clone(), + Host: c.host, + } + request.Header.Set("Connection", "Upgrade") + request.Header.Set("Upgrade", "websocket") + err = request.Write(conn) + if err != nil { + return nil, err + } + bufReader := std_bufio.NewReader(conn) + response, err := http.ReadResponse(bufReader, request) + if err != nil { + return nil, err + } + if response.StatusCode != 101 || + !strings.EqualFold(response.Header.Get("Connection"), "upgrade") || + !strings.EqualFold(response.Header.Get("Upgrade"), "websocket") { + return nil, E.New("unexpected status: ", response.Status) + } + if bufReader.Buffered() > 0 { + buffer := buf.NewSize(bufReader.Buffered()) + _, err = buffer.ReadFullFrom(bufReader, buffer.Len()) + if err != nil { + return nil, err + } + conn = bufio.NewCachedConn(conn, buffer) + } + return conn, nil +} diff --git a/transport/v2rayhttpupgrade/server.go b/transport/v2rayhttpupgrade/server.go new file mode 100644 index 0000000000..653778f91d --- /dev/null +++ b/transport/v2rayhttpupgrade/server.go @@ -0,0 +1,139 @@ +package v2rayhttpupgrade + +import ( + "context" + "net" + "net/http" + "os" + "strings" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/tls" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing/common" + E "github.com/sagernet/sing/common/exceptions" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + aTLS "github.com/sagernet/sing/common/tls" + sHttp "github.com/sagernet/sing/protocol/http" +) + +var _ adapter.V2RayServerTransport = (*Server)(nil) + +type Server struct { + ctx context.Context + tlsConfig tls.ServerConfig + handler adapter.V2RayServerTransportHandler + httpServer *http.Server + host string + path string + headers http.Header +} + +func NewServer(ctx context.Context, options option.V2RayHTTPUpgradeOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (*Server, error) { + server := &Server{ + ctx: ctx, + tlsConfig: tlsConfig, + handler: handler, + host: options.Host, + path: options.Path, + headers: options.Headers.Build(), + } + if !strings.HasPrefix(server.path, "/") { + server.path = "/" + server.path + } + server.httpServer = &http.Server{ + Handler: server, + ReadHeaderTimeout: C.TCPTimeout, + MaxHeaderBytes: http.DefaultMaxHeaderBytes, + BaseContext: func(net.Listener) context.Context { + return ctx + }, + TLSNextProto: make(map[string]func(*http.Server, *tls.STDConn, http.Handler)), + } + return server, nil +} + +type httpFlusher interface { + FlushError() error +} + +func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) { + host := request.Host + if len(s.host) > 0 && host != s.host { + s.invalidRequest(writer, request, http.StatusBadRequest, E.New("bad host: ", host)) + return + } + if !strings.HasPrefix(request.URL.Path, s.path) { + s.invalidRequest(writer, request, http.StatusNotFound, E.New("bad path: ", request.URL.Path)) + return + } + if request.Method != http.MethodGet { + s.invalidRequest(writer, request, http.StatusNotFound, E.New("bad method: ", request.Method)) + return + } + if !strings.EqualFold(request.Header.Get("Connection"), "upgrade") { + s.invalidRequest(writer, request, http.StatusNotFound, E.New("not a upgrade request")) + return + } + if !strings.EqualFold(request.Header.Get("Upgrade"), "websocket") { + s.invalidRequest(writer, request, http.StatusNotFound, E.New("not a websocket request")) + return + } + if request.Header.Get("Sec-WebSocket-Key") != "" { + s.invalidRequest(writer, request, http.StatusNotFound, E.New("real websocket request received")) + return + } + writer.Header().Set("Connection", "upgrade") + writer.Header().Set("Upgrade", "websocket") + writer.WriteHeader(http.StatusSwitchingProtocols) + if flusher, isFlusher := writer.(httpFlusher); isFlusher { + err := flusher.FlushError() + if err != nil { + s.invalidRequest(writer, request, http.StatusInternalServerError, E.New("flush response")) + } + } + hijacker, canHijack := writer.(http.Hijacker) + if !canHijack { + s.invalidRequest(writer, request, http.StatusInternalServerError, E.New("invalid connection, maybe HTTP/2")) + return + } + conn, _, err := hijacker.Hijack() + if err != nil { + s.invalidRequest(writer, request, http.StatusInternalServerError, E.Cause(err, "hijack failed")) + return + } + var metadata M.Metadata + metadata.Source = sHttp.SourceAddress(request) + s.handler.NewConnection(request.Context(), conn, metadata) +} + +func (s *Server) invalidRequest(writer http.ResponseWriter, request *http.Request, statusCode int, err error) { + if statusCode > 0 { + writer.WriteHeader(statusCode) + } + s.handler.NewError(request.Context(), E.Cause(err, "process connection from ", request.RemoteAddr)) +} + +func (s *Server) Network() []string { + return []string{N.NetworkTCP} +} + +func (s *Server) Serve(listener net.Listener) error { + if s.tlsConfig != nil { + if len(s.tlsConfig.NextProtos()) == 0 { + s.tlsConfig.SetNextProtos([]string{"http/1.1"}) + } + listener = aTLS.NewListener(listener, s.tlsConfig) + } + return s.httpServer.Serve(listener) +} + +func (s *Server) ServePacket(listener net.PacketConn) error { + return os.ErrInvalid +} + +func (s *Server) Close() error { + return common.Close(common.PtrOrNil(s.httpServer)) +} From 9863ad130cf5447028194f3214bfddb3da789d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Wed, 8 Nov 2023 12:09:22 +0800 Subject: [PATCH 05/11] Migrate multiplex and UoT server to inbound & Add tcp-brutal support for multiplex --- adapter/conn_router.go | 104 ++++++ adapter/router.go | 5 +- common/mux/client.go | 23 +- common/mux/protocol.go | 14 - common/mux/router.go | 65 ++++ common/mux/v2ray_legacy.go | 32 ++ common/uot/router.go | 53 +++ constant/speed.go | 3 + docs/configuration/inbound/hysteria2.zh.md | 2 +- docs/configuration/inbound/shadowsocks.md | 13 +- docs/configuration/inbound/shadowsocks.zh.md | 15 +- docs/configuration/inbound/trojan.md | 5 + docs/configuration/inbound/trojan.zh.md | 5 + docs/configuration/inbound/tuic.zh.md | 2 +- docs/configuration/inbound/vless.md | 5 + docs/configuration/inbound/vless.zh.md | 5 + docs/configuration/inbound/vmess.md | 5 + docs/configuration/inbound/vmess.zh.md | 5 + docs/configuration/outbound/hysteria2.zh.md | 2 +- docs/configuration/outbound/shadowsocks.md | 2 +- docs/configuration/outbound/shadowsocks.zh.md | 2 +- docs/configuration/outbound/trojan.md | 2 +- docs/configuration/outbound/trojan.zh.md | 2 +- docs/configuration/outbound/tuic.zh.md | 2 +- docs/configuration/outbound/vless.md | 5 + docs/configuration/outbound/vless.zh.md | 5 + docs/configuration/outbound/vmess.md | 4 +- docs/configuration/outbound/vmess.zh.md | 2 +- docs/configuration/shared/multiplex.md | 35 +- docs/configuration/shared/multiplex.zh.md | 35 +- docs/configuration/shared/tcp-brutal.md | 28 ++ docs/configuration/shared/tcp-brutal.zh.md | 28 ++ go.mod | 2 +- go.sum | 4 +- inbound/default.go | 2 +- inbound/http.go | 3 +- inbound/mixed.go | 3 +- inbound/naive.go | 3 +- inbound/shadowsocks.go | 12 +- inbound/shadowsocks_multi.go | 14 +- inbound/shadowsocks_relay.go | 9 +- inbound/socks.go | 3 +- inbound/trojan.go | 5 + inbound/tuic.go | 3 +- inbound/vless.go | 10 +- inbound/vmess.go | 11 +- mkdocs.yml | 1 + option/multiplex.go | 23 ++ option/outbound.go | 9 - option/shadowsocks.go | 15 +- option/simple.go | 10 +- option/trojan.go | 11 +- option/vless.go | 21 +- option/vmess.go | 27 +- outbound/shadowsocks.go | 4 +- outbound/socks.go | 2 +- outbound/trojan.go | 2 +- outbound/vless.go | 2 +- outbound/vmess.go | 2 +- route/router.go | 27 +- test/brutal_test.go | 346 ++++++++++++++++++ test/go.mod | 2 +- test/go.sum | 4 +- test/mux_test.go | 21 +- test/shadowsocks_test.go | 2 +- 65 files changed, 972 insertions(+), 158 deletions(-) create mode 100644 adapter/conn_router.go delete mode 100644 common/mux/protocol.go create mode 100644 common/mux/router.go create mode 100644 common/mux/v2ray_legacy.go create mode 100644 common/uot/router.go create mode 100644 constant/speed.go create mode 100644 docs/configuration/shared/tcp-brutal.md create mode 100644 docs/configuration/shared/tcp-brutal.zh.md create mode 100644 option/multiplex.go create mode 100644 test/brutal_test.go diff --git a/adapter/conn_router.go b/adapter/conn_router.go new file mode 100644 index 0000000000..a87c45e8c5 --- /dev/null +++ b/adapter/conn_router.go @@ -0,0 +1,104 @@ +package adapter + +import ( + "context" + "net" + + "github.com/sagernet/sing/common/logger" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" +) + +type ConnectionRouter interface { + RouteConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error + RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error +} + +func NewRouteHandler( + metadata InboundContext, + router ConnectionRouter, + logger logger.ContextLogger, +) UpstreamHandlerAdapter { + return &routeHandlerWrapper{ + metadata: metadata, + router: router, + logger: logger, + } +} + +func NewRouteContextHandler( + router ConnectionRouter, + logger logger.ContextLogger, +) UpstreamHandlerAdapter { + return &routeContextHandlerWrapper{ + router: router, + logger: logger, + } +} + +var _ UpstreamHandlerAdapter = (*routeHandlerWrapper)(nil) + +type routeHandlerWrapper struct { + metadata InboundContext + router ConnectionRouter + logger logger.ContextLogger +} + +func (w *routeHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { + myMetadata := w.metadata + if metadata.Source.IsValid() { + myMetadata.Source = metadata.Source + } + if metadata.Destination.IsValid() { + myMetadata.Destination = metadata.Destination + } + return w.router.RouteConnection(ctx, conn, myMetadata) +} + +func (w *routeHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error { + myMetadata := w.metadata + if metadata.Source.IsValid() { + myMetadata.Source = metadata.Source + } + if metadata.Destination.IsValid() { + myMetadata.Destination = metadata.Destination + } + return w.router.RoutePacketConnection(ctx, conn, myMetadata) +} + +func (w *routeHandlerWrapper) NewError(ctx context.Context, err error) { + w.logger.ErrorContext(ctx, err) +} + +var _ UpstreamHandlerAdapter = (*routeContextHandlerWrapper)(nil) + +type routeContextHandlerWrapper struct { + router ConnectionRouter + logger logger.ContextLogger +} + +func (w *routeContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { + myMetadata := ContextFrom(ctx) + if metadata.Source.IsValid() { + myMetadata.Source = metadata.Source + } + if metadata.Destination.IsValid() { + myMetadata.Destination = metadata.Destination + } + return w.router.RouteConnection(ctx, conn, *myMetadata) +} + +func (w *routeContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error { + myMetadata := ContextFrom(ctx) + if metadata.Source.IsValid() { + myMetadata.Source = metadata.Source + } + if metadata.Destination.IsValid() { + myMetadata.Destination = metadata.Destination + } + return w.router.RoutePacketConnection(ctx, conn, *myMetadata) +} + +func (w *routeContextHandlerWrapper) NewError(ctx context.Context, err error) { + w.logger.ErrorContext(ctx, err) +} diff --git a/adapter/router.go b/adapter/router.go index ec23d9311f..ab2d916c65 100644 --- a/adapter/router.go +++ b/adapter/router.go @@ -2,14 +2,12 @@ package adapter import ( "context" - "net" "net/netip" "github.com/sagernet/sing-box/common/geoip" "github.com/sagernet/sing-dns" "github.com/sagernet/sing-tun" "github.com/sagernet/sing/common/control" - N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/service" mdns "github.com/miekg/dns" @@ -24,8 +22,7 @@ type Router interface { FakeIPStore() FakeIPStore - RouteConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error - RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error + ConnectionRouter GeoIPReader() *geoip.Reader LoadGeosite(code string) (Rule, error) diff --git a/common/mux/client.go b/common/mux/client.go index 36dd31379b..6f201deac4 100644 --- a/common/mux/client.go +++ b/common/mux/client.go @@ -1,21 +1,42 @@ package mux import ( + C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-mux" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" N "github.com/sagernet/sing/common/network" ) -func NewClientWithOptions(dialer N.Dialer, options option.MultiplexOptions) (*Client, error) { +type Client = mux.Client + +func NewClientWithOptions(dialer N.Dialer, logger logger.Logger, options option.OutboundMultiplexOptions) (*Client, error) { if !options.Enabled { return nil, nil } + var brutalOptions mux.BrutalOptions + if options.Brutal != nil && options.Brutal.Enabled { + brutalOptions = mux.BrutalOptions{ + Enabled: true, + SendBPS: uint64(options.Brutal.UpMbps * C.MbpsToBps), + ReceiveBPS: uint64(options.Brutal.DownMbps * C.MbpsToBps), + } + if brutalOptions.SendBPS < mux.BrutalMinSpeedBPS { + return nil, E.New("brutal: invalid upload speed") + } + if brutalOptions.ReceiveBPS < mux.BrutalMinSpeedBPS { + return nil, E.New("brutal: invalid download speed") + } + } return mux.NewClient(mux.Options{ Dialer: dialer, + Logger: logger, Protocol: options.Protocol, MaxConnections: options.MaxConnections, MinStreams: options.MinStreams, MaxStreams: options.MaxStreams, Padding: options.Padding, + Brutal: brutalOptions, }) } diff --git a/common/mux/protocol.go b/common/mux/protocol.go deleted file mode 100644 index abb0e26865..0000000000 --- a/common/mux/protocol.go +++ /dev/null @@ -1,14 +0,0 @@ -package mux - -import ( - "github.com/sagernet/sing-mux" -) - -type ( - Client = mux.Client -) - -var ( - Destination = mux.Destination - HandleConnection = mux.HandleConnection -) diff --git a/common/mux/router.go b/common/mux/router.go new file mode 100644 index 0000000000..8a2296852b --- /dev/null +++ b/common/mux/router.go @@ -0,0 +1,65 @@ +package mux + +import ( + "context" + "net" + + "github.com/sagernet/sing-box/adapter" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing-mux" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" + N "github.com/sagernet/sing/common/network" +) + +type Router struct { + router adapter.ConnectionRouter + service *mux.Service +} + +func NewRouterWithOptions(router adapter.ConnectionRouter, logger logger.ContextLogger, options option.InboundMultiplexOptions) (adapter.ConnectionRouter, error) { + if !options.Enabled { + return router, nil + } + var brutalOptions mux.BrutalOptions + if options.Brutal != nil && options.Brutal.Enabled { + brutalOptions = mux.BrutalOptions{ + Enabled: true, + SendBPS: uint64(options.Brutal.UpMbps * C.MbpsToBps), + ReceiveBPS: uint64(options.Brutal.DownMbps * C.MbpsToBps), + } + if brutalOptions.SendBPS < mux.BrutalMinSpeedBPS { + return nil, E.New("brutal: invalid upload speed") + } + if brutalOptions.ReceiveBPS < mux.BrutalMinSpeedBPS { + return nil, E.New("brutal: invalid download speed") + } + } + service, err := mux.NewService(mux.ServiceOptions{ + NewStreamContext: func(ctx context.Context, conn net.Conn) context.Context { + return log.ContextWithNewID(ctx) + }, + Logger: logger, + Handler: adapter.NewRouteContextHandler(router, logger), + Padding: options.Padding, + Brutal: brutalOptions, + }) + if err != nil { + return nil, err + } + return &Router{router, service}, nil +} + +func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { + if metadata.Destination == mux.Destination { + return r.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, adapter.UpstreamMetadata(metadata)) + } else { + return r.router.RouteConnection(ctx, conn, metadata) + } +} + +func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { + return r.router.RoutePacketConnection(ctx, conn, metadata) +} diff --git a/common/mux/v2ray_legacy.go b/common/mux/v2ray_legacy.go new file mode 100644 index 0000000000..f53aff2db4 --- /dev/null +++ b/common/mux/v2ray_legacy.go @@ -0,0 +1,32 @@ +package mux + +import ( + "context" + "net" + + "github.com/sagernet/sing-box/adapter" + vmess "github.com/sagernet/sing-vmess" + "github.com/sagernet/sing/common/logger" + N "github.com/sagernet/sing/common/network" +) + +type V2RayLegacyRouter struct { + router adapter.ConnectionRouter + logger logger.ContextLogger +} + +func NewV2RayLegacyRouter(router adapter.ConnectionRouter, logger logger.ContextLogger) adapter.ConnectionRouter { + return &V2RayLegacyRouter{router, logger} +} + +func (r *V2RayLegacyRouter) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { + if metadata.Destination.Fqdn == vmess.MuxDestination.Fqdn { + r.logger.InfoContext(ctx, "inbound legacy multiplex connection") + return vmess.HandleMuxConnection(ctx, conn, adapter.NewRouteHandler(metadata, r.router, r.logger)) + } + return r.router.RouteConnection(ctx, conn, metadata) +} + +func (r *V2RayLegacyRouter) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { + return r.router.RoutePacketConnection(ctx, conn, metadata) +} diff --git a/common/uot/router.go b/common/uot/router.go new file mode 100644 index 0000000000..fb2d23d557 --- /dev/null +++ b/common/uot/router.go @@ -0,0 +1,53 @@ +package uot + +import ( + "context" + "net" + "net/netip" + + "github.com/sagernet/sing-box/adapter" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/common/uot" +) + +var _ adapter.ConnectionRouter = (*Router)(nil) + +type Router struct { + router adapter.ConnectionRouter + logger logger.ContextLogger +} + +func NewRouter(router adapter.ConnectionRouter, logger logger.ContextLogger) *Router { + return &Router{router, logger} +} + +func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { + switch metadata.Destination.Fqdn { + case uot.MagicAddress: + request, err := uot.ReadRequest(conn) + if err != nil { + return E.Cause(err, "read UoT request") + } + if request.IsConnect { + r.logger.InfoContext(ctx, "inbound UoT connect connection to ", request.Destination) + } else { + r.logger.InfoContext(ctx, "inbound UoT connection to ", request.Destination) + } + metadata.Domain = metadata.Destination.Fqdn + metadata.Destination = request.Destination + return r.router.RoutePacketConnection(ctx, uot.NewConn(conn, *request), metadata) + case uot.LegacyMagicAddress: + r.logger.InfoContext(ctx, "inbound legacy UoT connection") + metadata.Domain = metadata.Destination.Fqdn + metadata.Destination = M.Socksaddr{Addr: netip.IPv4Unspecified()} + return r.RoutePacketConnection(ctx, uot.NewConn(conn, uot.Request{}), metadata) + } + return r.router.RouteConnection(ctx, conn, metadata) +} + +func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { + return r.router.RoutePacketConnection(ctx, conn, metadata) +} diff --git a/constant/speed.go b/constant/speed.go new file mode 100644 index 0000000000..7a2ec130a2 --- /dev/null +++ b/constant/speed.go @@ -0,0 +1,3 @@ +package constant + +const MbpsToBps = 125000 diff --git a/docs/configuration/inbound/hysteria2.zh.md b/docs/configuration/inbound/hysteria2.zh.md index d21a13d09e..f43188c09f 100644 --- a/docs/configuration/inbound/hysteria2.zh.md +++ b/docs/configuration/inbound/hysteria2.zh.md @@ -62,7 +62,7 @@ Hysteria 用户 #### ignore_client_bandwidth -命令客户端使用 BBR 流量控制算法而不是 Hysteria CC。 +命令客户端使用 BBR 拥塞控制算法而不是 Hysteria CC。 与 `up_mbps` 和 `down_mbps` 冲突。 diff --git a/docs/configuration/inbound/shadowsocks.md b/docs/configuration/inbound/shadowsocks.md index 0349d01c72..415a59138e 100644 --- a/docs/configuration/inbound/shadowsocks.md +++ b/docs/configuration/inbound/shadowsocks.md @@ -8,7 +8,8 @@ ... // Listen Fields "method": "2022-blake3-aes-128-gcm", - "password": "8JCsPssfgS8tiRwiMlhARg==" + "password": "8JCsPssfgS8tiRwiMlhARg==", + "multiplex": {} } ``` @@ -23,7 +24,8 @@ "name": "sekai", "password": "PCD2Z4o12bKUoFa3cC97Hw==" } - ] + ], + "multiplex": {} } ``` @@ -41,7 +43,8 @@ "server_port": 8080, "password": "PCD2Z4o12bKUoFa3cC97Hw==" } - ] + ], + "multiplex": {} } ``` @@ -82,3 +85,7 @@ Both if empty. | none | / | | 2022 methods | `sing-box generate rand --base64 ` | | other methods | any string | + +#### multiplex + +See [Multiplex](/configuration/shared/multiplex#inbound) for details. diff --git a/docs/configuration/inbound/shadowsocks.zh.md b/docs/configuration/inbound/shadowsocks.zh.md index 11edd05756..36a292bb0c 100644 --- a/docs/configuration/inbound/shadowsocks.zh.md +++ b/docs/configuration/inbound/shadowsocks.zh.md @@ -8,7 +8,8 @@ ... // 监听字段 "method": "2022-blake3-aes-128-gcm", - "password": "8JCsPssfgS8tiRwiMlhARg==" + "password": "8JCsPssfgS8tiRwiMlhARg==", + "multiplex": {} } ``` @@ -23,7 +24,8 @@ "name": "sekai", "password": "PCD2Z4o12bKUoFa3cC97Hw==" } - ] + ], + "multiplex": {} } ``` @@ -41,7 +43,8 @@ "server_port": 8080, "password": "PCD2Z4o12bKUoFa3cC97Hw==" } - ] + ], + "multiplex": {} } ``` @@ -81,4 +84,8 @@ See [Listen Fields](/configuration/shared/listen) for details. |---------------|------------------------------------------| | none | / | | 2022 methods | `sing-box generate rand --base64 <密钥长度>` | -| other methods | 任意字符串 | \ No newline at end of file +| other methods | 任意字符串 | + +#### multiplex + +参阅 [多路复用](/zh/configuration/shared/multiplex#inbound)。 diff --git a/docs/configuration/inbound/trojan.md b/docs/configuration/inbound/trojan.md index cdaf8f5030..787d2b11bf 100644 --- a/docs/configuration/inbound/trojan.md +++ b/docs/configuration/inbound/trojan.md @@ -24,6 +24,7 @@ "server_port": 8081 } }, + "multiplex": {}, "transport": {} } ``` @@ -58,6 +59,10 @@ Fallback server configuration for specified ALPN. If not empty, TLS fallback requests with ALPN not in this table will be rejected. +#### multiplex + +See [Multiplex](/configuration/shared/multiplex#inbound) for details. + #### transport V2Ray Transport configuration, see [V2Ray Transport](/configuration/shared/v2ray-transport). diff --git a/docs/configuration/inbound/trojan.zh.md b/docs/configuration/inbound/trojan.zh.md index 6cc8c4756e..f52422af82 100644 --- a/docs/configuration/inbound/trojan.zh.md +++ b/docs/configuration/inbound/trojan.zh.md @@ -24,6 +24,7 @@ "server_port": 8081 } }, + "multiplex": {}, "transport": {} } ``` @@ -60,6 +61,10 @@ TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。 如果不为空,ALPN 不在此列表中的 TLS 回退请求将被拒绝。 +#### multiplex + +参阅 [多路复用](/zh/configuration/shared/multiplex#inbound)。 + #### transport V2Ray 传输配置,参阅 [V2Ray 传输层](/zh/configuration/shared/v2ray-transport)。 \ No newline at end of file diff --git a/docs/configuration/inbound/tuic.zh.md b/docs/configuration/inbound/tuic.zh.md index 9a6f395e37..60a7ccf6fd 100644 --- a/docs/configuration/inbound/tuic.zh.md +++ b/docs/configuration/inbound/tuic.zh.md @@ -48,7 +48,7 @@ TUIC 用户密码 #### congestion_control -QUIC 流量控制算法 +QUIC 拥塞控制算法 可选值: `cubic`, `new_reno`, `bbr` diff --git a/docs/configuration/inbound/vless.md b/docs/configuration/inbound/vless.md index a8d0c426da..f78ae01c19 100644 --- a/docs/configuration/inbound/vless.md +++ b/docs/configuration/inbound/vless.md @@ -15,6 +15,7 @@ } ], "tls": {}, + "multiplex": {}, "transport": {} } ``` @@ -49,6 +50,10 @@ Available values: TLS configuration, see [TLS](/configuration/shared/tls/#inbound). +#### multiplex + +See [Multiplex](/configuration/shared/multiplex#inbound) for details. + #### transport V2Ray Transport configuration, see [V2Ray Transport](/configuration/shared/v2ray-transport). diff --git a/docs/configuration/inbound/vless.zh.md b/docs/configuration/inbound/vless.zh.md index 3ce1261c9a..4beecd6faa 100644 --- a/docs/configuration/inbound/vless.zh.md +++ b/docs/configuration/inbound/vless.zh.md @@ -15,6 +15,7 @@ } ], "tls": {}, + "multiplex": {}, "transport": {} } ``` @@ -49,6 +50,10 @@ VLESS 子协议。 TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。 +#### multiplex + +参阅 [多路复用](/zh/configuration/shared/multiplex#inbound)。 + #### transport V2Ray 传输配置,参阅 [V2Ray 传输层](/zh/configuration/shared/v2ray-transport)。 diff --git a/docs/configuration/inbound/vmess.md b/docs/configuration/inbound/vmess.md index 2439d6e148..0e559d1ef6 100644 --- a/docs/configuration/inbound/vmess.md +++ b/docs/configuration/inbound/vmess.md @@ -15,6 +15,7 @@ } ], "tls": {}, + "multiplex": {}, "transport": {} } ``` @@ -44,6 +45,10 @@ VMess users. TLS configuration, see [TLS](/configuration/shared/tls/#inbound). +#### multiplex + +See [Multiplex](/configuration/shared/multiplex#inbound) for details. + #### transport V2Ray Transport configuration, see [V2Ray Transport](/configuration/shared/v2ray-transport). diff --git a/docs/configuration/inbound/vmess.zh.md b/docs/configuration/inbound/vmess.zh.md index ae90145b1c..9554ab79e0 100644 --- a/docs/configuration/inbound/vmess.zh.md +++ b/docs/configuration/inbound/vmess.zh.md @@ -15,6 +15,7 @@ } ], "tls": {}, + "multiplex": {}, "transport": {} } ``` @@ -44,6 +45,10 @@ VMess 用户。 TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。 +#### multiplex + +参阅 [多路复用](/zh/configuration/shared/multiplex#inbound)。 + #### transport V2Ray 传输配置,参阅 [V2Ray 传输层](/zh/configuration/shared/v2ray-transport)。 diff --git a/docs/configuration/outbound/hysteria2.zh.md b/docs/configuration/outbound/hysteria2.zh.md index e9d1cb2d30..1e490a63af 100644 --- a/docs/configuration/outbound/hysteria2.zh.md +++ b/docs/configuration/outbound/hysteria2.zh.md @@ -44,7 +44,7 @@ 最大带宽。 -如果为空,将使用 BBR 流量控制算法而不是 Hysteria CC。 +如果为空,将使用 BBR 拥塞控制算法而不是 Hysteria CC。 #### obfs.type diff --git a/docs/configuration/outbound/shadowsocks.md b/docs/configuration/outbound/shadowsocks.md index e004d77b1a..6fc9348408 100644 --- a/docs/configuration/outbound/shadowsocks.md +++ b/docs/configuration/outbound/shadowsocks.md @@ -95,7 +95,7 @@ Conflict with `multiplex`. #### multiplex -Multiplex configuration, see [Multiplex](/configuration/shared/multiplex). +See [Multiplex](/configuration/shared/multiplex#outbound) for details. ### Dial Fields diff --git a/docs/configuration/outbound/shadowsocks.zh.md b/docs/configuration/outbound/shadowsocks.zh.md index 16c627e325..6d9b7a5c85 100644 --- a/docs/configuration/outbound/shadowsocks.zh.md +++ b/docs/configuration/outbound/shadowsocks.zh.md @@ -95,7 +95,7 @@ UDP over TCP 配置。 #### multiplex -多路复用配置, 参阅 [多路复用](/zh/configuration/shared/multiplex)。 +参阅 [多路复用](/zh/configuration/shared/multiplex#outbound)。 ### 拨号字段 diff --git a/docs/configuration/outbound/trojan.md b/docs/configuration/outbound/trojan.md index cee13401a6..34b16c7d38 100644 --- a/docs/configuration/outbound/trojan.md +++ b/docs/configuration/outbound/trojan.md @@ -51,7 +51,7 @@ TLS configuration, see [TLS](/configuration/shared/tls/#outbound). #### multiplex -Multiplex configuration, see [Multiplex](/configuration/shared/multiplex). +See [Multiplex](/configuration/shared/multiplex#outbound) for details. #### transport diff --git a/docs/configuration/outbound/trojan.zh.md b/docs/configuration/outbound/trojan.zh.md index ac9191873f..55bb970911 100644 --- a/docs/configuration/outbound/trojan.zh.md +++ b/docs/configuration/outbound/trojan.zh.md @@ -51,7 +51,7 @@ TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。 #### multiplex -多路复用配置, 参阅 [多路复用](/zh/configuration/shared/multiplex)。 +参阅 [多路复用](/zh/configuration/shared/multiplex#outbound)。 #### transport diff --git a/docs/configuration/outbound/tuic.zh.md b/docs/configuration/outbound/tuic.zh.md index ce5a18999b..11d44448af 100644 --- a/docs/configuration/outbound/tuic.zh.md +++ b/docs/configuration/outbound/tuic.zh.md @@ -51,7 +51,7 @@ TUIC 用户密码 #### congestion_control -QUIC 流量控制算法 +QUIC 拥塞控制算法 可选值: `cubic`, `new_reno`, `bbr` diff --git a/docs/configuration/outbound/vless.md b/docs/configuration/outbound/vless.md index 5fc69bf407..0d2fadc646 100644 --- a/docs/configuration/outbound/vless.md +++ b/docs/configuration/outbound/vless.md @@ -12,6 +12,7 @@ "network": "tcp", "tls": {}, "packet_encoding": "", + "multiplex": {}, "transport": {}, ... // Dial Fields @@ -68,6 +69,10 @@ UDP packet encoding, xudp is used by default. | packetaddr | Supported by v2ray 5+ | | xudp | Supported by xray | +#### multiplex + +See [Multiplex](/configuration/shared/multiplex#outbound) for details. + #### transport V2Ray Transport configuration, see [V2Ray Transport](/configuration/shared/v2ray-transport). diff --git a/docs/configuration/outbound/vless.zh.md b/docs/configuration/outbound/vless.zh.md index 75713bf514..3d3f24ee65 100644 --- a/docs/configuration/outbound/vless.zh.md +++ b/docs/configuration/outbound/vless.zh.md @@ -12,6 +12,7 @@ "network": "tcp", "tls": {}, "packet_encoding": "", + "multiplex": {}, "transport": {}, ... // 拨号字段 @@ -68,6 +69,10 @@ UDP 包编码,默认使用 xudp。 | packetaddr | 由 v2ray 5+ 支持 | | xudp | 由 xray 支持 | +#### multiplex + +参阅 [多路复用](/zh/configuration/shared/multiplex#outbound)。 + #### transport V2Ray 传输配置,参阅 [V2Ray 传输层](/zh/configuration/shared/v2ray-transport)。 diff --git a/docs/configuration/outbound/vmess.md b/docs/configuration/outbound/vmess.md index 4522047724..ac29559e72 100644 --- a/docs/configuration/outbound/vmess.md +++ b/docs/configuration/outbound/vmess.md @@ -15,8 +15,8 @@ "network": "tcp", "tls": {}, "packet_encoding": "", - "multiplex": {}, "transport": {}, + "multiplex": {}, ... // Dial Fields } @@ -96,7 +96,7 @@ UDP packet encoding. #### multiplex -Multiplex configuration, see [Multiplex](/configuration/shared/multiplex). +See [Multiplex](/configuration/shared/multiplex#outbound) for details. #### transport diff --git a/docs/configuration/outbound/vmess.zh.md b/docs/configuration/outbound/vmess.zh.md index 16fcfc7acf..dbf1612e51 100644 --- a/docs/configuration/outbound/vmess.zh.md +++ b/docs/configuration/outbound/vmess.zh.md @@ -96,7 +96,7 @@ UDP 包编码。 #### multiplex -多路复用配置, 参阅 [多路复用](/zh/configuration/shared/multiplex)。 +参阅 [多路复用](/zh/configuration/shared/multiplex#outbound)。 #### transport diff --git a/docs/configuration/shared/multiplex.md b/docs/configuration/shared/multiplex.md index 833efaff3f..eab2116381 100644 --- a/docs/configuration/shared/multiplex.md +++ b/docs/configuration/shared/multiplex.md @@ -1,8 +1,14 @@ -### Server Requirements +### Inbound -`sing-box` :) +```json +{ + "enabled": true, + "padding": false, + "brutal": {} +} +``` -### Structure +### Outbound ```json { @@ -11,11 +17,27 @@ "max_connections": 4, "min_streams": 4, "max_streams": 0, - "padding": false + "padding": false, + "brutal": {} } ``` -### Fields + +### Inbound Fields + +#### enabled + +Enable multiplex support. + +#### padding + +If enabled, non-padded connections will be rejected. + +#### brutal + +See [TCP Brutal](/configuration/shared/tcp-brutal) for details. + +### Outbound Fields #### enabled @@ -59,3 +81,6 @@ Conflict with `max_connections` and `min_streams`. Enable padding. +#### brutal + +See [TCP Brutal](/configuration/shared/tcp-brutal) for details. diff --git a/docs/configuration/shared/multiplex.zh.md b/docs/configuration/shared/multiplex.zh.md index 99a0ba0354..ae1cad64e7 100644 --- a/docs/configuration/shared/multiplex.zh.md +++ b/docs/configuration/shared/multiplex.zh.md @@ -1,8 +1,14 @@ -### 服务器要求 +### 入站 -`sing-box` :) +```json +{ + "enabled": true, + "padding": false, + "brutal": {} +} +``` -### 结构 +### 出站 ```json { @@ -10,11 +16,27 @@ "protocol": "smux", "max_connections": 4, "min_streams": 4, - "max_streams": 0 + "max_streams": 0, + "padding": false, + "brutal": {} } ``` -### 字段 +### 入站字段 + +#### enabled + +启用多路复用支持。 + +#### padding + +如果启用,将拒绝非填充连接。 + +#### brutal + +参阅 [TCP Brutal](/zh/configuration/shared/tcp-brutal)。 + +### 出站字段 #### enabled @@ -58,3 +80,6 @@ 启用填充。 +#### brutal + +参阅 [TCP Brutal](/zh/configuration/shared/tcp-brutal)。 \ No newline at end of file diff --git a/docs/configuration/shared/tcp-brutal.md b/docs/configuration/shared/tcp-brutal.md new file mode 100644 index 0000000000..d248a463f1 --- /dev/null +++ b/docs/configuration/shared/tcp-brutal.md @@ -0,0 +1,28 @@ +### Server Requirements + +* Linux +* `brutal` congestion control algorithm kernel module installed + +See [tcp-brutal](https://github.com/apernet/tcp-brutal) for details. + +### Structure + +```json +{ + "enabled": true, + "up_mbps": 100, + "down_mbps": 100 +} +``` + +### Fields + +#### enabled + +Enable TCP Brutal congestion control algorithm。 + +#### up_mbps, down_mbps + +==Required== + +Upload and download bandwidth, in Mbps. \ No newline at end of file diff --git a/docs/configuration/shared/tcp-brutal.zh.md b/docs/configuration/shared/tcp-brutal.zh.md new file mode 100644 index 0000000000..efb8c51f60 --- /dev/null +++ b/docs/configuration/shared/tcp-brutal.zh.md @@ -0,0 +1,28 @@ +### 服务器要求 + +* Linux +* `brutal` 拥塞控制算法内核模块已安装 + +参阅 [tcp-brutal](https://github.com/apernet/tcp-brutal)。 + +### 结构 + +```json +{ + "enabled": true, + "up_mbps": 100, + "down_mbps": 100 +} +``` + +### 字段 + +#### enabled + +启用 TCP Brutal 拥塞控制算法。 + +#### up_mbps, down_mbps + +==必填== + +上传和下载带宽,以 Mbps 为单位。 diff --git a/go.mod b/go.mod index e5a3ea30ca..a6b9461c53 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358 - github.com/sagernet/sing-mux v0.1.4 + github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 github.com/sagernet/sing-quic v0.1.4-0.20231111111624-370e6abf6769 github.com/sagernet/sing-shadowsocks v0.2.5 github.com/sagernet/sing-shadowsocks2 v0.1.4 diff --git a/go.sum b/go.sum index e2872ed0ff..df3241c4e5 100644 --- a/go.sum +++ b/go.sum @@ -118,8 +118,8 @@ github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c h1:uask61Pxc3nGqs github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358 h1:psJQg/KXVQTupFFR1liaCAucW+NwCDhe1oYfkmz8EJ8= github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358/go.mod h1:vtUimtf7Nq9EdvD5WTpfCr69KL1M7bcgOVKiYBiAY/c= -github.com/sagernet/sing-mux v0.1.4 h1:BPNPOQr6HkXG3iY/BrfvUKUl+A7gYsGKVSxvoR3PO50= -github.com/sagernet/sing-mux v0.1.4/go.mod h1:dKvcu/sb3fZ88uGv9vzAqUej6J4W+pHu5GqjRuFwAWs= +github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 h1:ncKb5tVOsCQgCsv6UpsA0jinbNb5OQ5GMPJlyQP3EHM= +github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07/go.mod h1:u/MZf32xPG8jEKe3t+xUV67EBnKtDtCaPhsJQOQGUYU= github.com/sagernet/sing-quic v0.1.4-0.20231111111624-370e6abf6769 h1:V84NPJKirL/oDkvUh9Dor2gMZ+TTNHK3jcedqRkgcQA= github.com/sagernet/sing-quic v0.1.4-0.20231111111624-370e6abf6769/go.mod h1:iggBfFE2ojS2yYQU8Hnrkm029RsHPdYYIKWqeCI64HE= github.com/sagernet/sing-shadowsocks v0.2.5 h1:qxIttos4xu6ii7MTVJYA8EFQR7Q3KG6xMqmLJIFtBaY= diff --git a/inbound/default.go b/inbound/default.go index de538e1701..44c580deb9 100644 --- a/inbound/default.go +++ b/inbound/default.go @@ -22,7 +22,7 @@ type myInboundAdapter struct { protocol string network []string ctx context.Context - router adapter.Router + router adapter.ConnectionRouter logger log.ContextLogger tag string listenOptions option.ListenOptions diff --git a/inbound/http.go b/inbound/http.go index 14a614b1af..fa6c3d5867 100644 --- a/inbound/http.go +++ b/inbound/http.go @@ -8,6 +8,7 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/tls" + "github.com/sagernet/sing-box/common/uot" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" @@ -35,7 +36,7 @@ func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogge protocol: C.TypeHTTP, network: []string{N.NetworkTCP}, ctx: ctx, - router: router, + router: uot.NewRouter(router, logger), logger: logger, tag: tag, listenOptions: options.ListenOptions, diff --git a/inbound/mixed.go b/inbound/mixed.go index fceefe4d84..8c3bf3b4f9 100644 --- a/inbound/mixed.go +++ b/inbound/mixed.go @@ -7,6 +7,7 @@ import ( "os" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/uot" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" @@ -37,7 +38,7 @@ func NewMixed(ctx context.Context, router adapter.Router, logger log.ContextLogg protocol: C.TypeMixed, network: []string{N.NetworkTCP}, ctx: ctx, - router: router, + router: uot.NewRouter(router, logger), logger: logger, tag: tag, listenOptions: options.ListenOptions, diff --git a/inbound/naive.go b/inbound/naive.go index 0ea2bbb5df..1ff159d100 100644 --- a/inbound/naive.go +++ b/inbound/naive.go @@ -13,6 +13,7 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/tls" + "github.com/sagernet/sing-box/common/uot" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/include" "github.com/sagernet/sing-box/log" @@ -43,7 +44,7 @@ func NewNaive(ctx context.Context, router adapter.Router, logger log.ContextLogg protocol: C.TypeNaive, network: options.Network.Build(), ctx: ctx, - router: router, + router: uot.NewRouter(router, logger), logger: logger, tag: tag, listenOptions: options.ListenOptions, diff --git a/inbound/shadowsocks.go b/inbound/shadowsocks.go index 822a5f57f8..a45b6daf4b 100644 --- a/inbound/shadowsocks.go +++ b/inbound/shadowsocks.go @@ -6,6 +6,8 @@ import ( "os" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/mux" + "github.com/sagernet/sing-box/common/uot" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" @@ -48,21 +50,27 @@ func newShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte protocol: C.TypeShadowsocks, network: options.Network.Build(), ctx: ctx, - router: router, + router: uot.NewRouter(router, logger), logger: logger, tag: tag, listenOptions: options.ListenOptions, }, } + inbound.connHandler = inbound inbound.packetHandler = inbound + var err error + inbound.router, err = mux.NewRouterWithOptions(inbound.router, logger, common.PtrValueOrDefault(options.Multiplex)) + if err != nil { + return nil, err + } + var udpTimeout int64 if options.UDPTimeout != 0 { udpTimeout = options.UDPTimeout } else { udpTimeout = int64(C.UDPTimeout.Seconds()) } - var err error switch { case options.Method == shadowsocks.MethodNone: inbound.service = shadowsocks.NewNoneService(options.UDPTimeout, inbound.upstreamContextHandler()) diff --git a/inbound/shadowsocks_multi.go b/inbound/shadowsocks_multi.go index 6a1baac264..c3c7d2abd3 100644 --- a/inbound/shadowsocks_multi.go +++ b/inbound/shadowsocks_multi.go @@ -6,6 +6,8 @@ import ( "os" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/mux" + "github.com/sagernet/sing-box/common/uot" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" @@ -38,7 +40,7 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log. protocol: C.TypeShadowsocks, network: options.Network.Build(), ctx: ctx, - router: router, + router: uot.NewRouter(router, logger), logger: logger, tag: tag, listenOptions: options.ListenOptions, @@ -46,16 +48,18 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log. } inbound.connHandler = inbound inbound.packetHandler = inbound + var err error + inbound.router, err = mux.NewRouterWithOptions(inbound.router, logger, common.PtrValueOrDefault(options.Multiplex)) + if err != nil { + return nil, err + } var udpTimeout int64 if options.UDPTimeout != 0 { udpTimeout = options.UDPTimeout } else { udpTimeout = int64(C.UDPTimeout.Seconds()) } - var ( - service shadowsocks.MultiService[int] - err error - ) + var service shadowsocks.MultiService[int] if common.Contains(shadowaead_2022.List, options.Method) { service, err = shadowaead_2022.NewMultiServiceWithPassword[int]( options.Method, diff --git a/inbound/shadowsocks_relay.go b/inbound/shadowsocks_relay.go index 2f624447be..44f39c9f6a 100644 --- a/inbound/shadowsocks_relay.go +++ b/inbound/shadowsocks_relay.go @@ -6,6 +6,8 @@ import ( "os" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/mux" + "github.com/sagernet/sing-box/common/uot" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" @@ -34,7 +36,7 @@ func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log. protocol: C.TypeShadowsocks, network: options.Network.Build(), ctx: ctx, - router: router, + router: uot.NewRouter(router, logger), logger: logger, tag: tag, listenOptions: options.ListenOptions, @@ -43,6 +45,11 @@ func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log. } inbound.connHandler = inbound inbound.packetHandler = inbound + var err error + inbound.router, err = mux.NewRouterWithOptions(inbound.router, logger, common.PtrValueOrDefault(options.Multiplex)) + if err != nil { + return nil, err + } var udpTimeout int64 if options.UDPTimeout != 0 { udpTimeout = options.UDPTimeout diff --git a/inbound/socks.go b/inbound/socks.go index 8cc256efee..65dfb1e651 100644 --- a/inbound/socks.go +++ b/inbound/socks.go @@ -6,6 +6,7 @@ import ( "os" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/uot" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" @@ -30,7 +31,7 @@ func NewSocks(ctx context.Context, router adapter.Router, logger log.ContextLogg protocol: C.TypeSOCKS, network: []string{N.NetworkTCP}, ctx: ctx, - router: router, + router: uot.NewRouter(router, logger), logger: logger, tag: tag, listenOptions: options.ListenOptions, diff --git a/inbound/trojan.go b/inbound/trojan.go index 82432131b0..203b6d3927 100644 --- a/inbound/trojan.go +++ b/inbound/trojan.go @@ -6,6 +6,7 @@ import ( "os" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/mux" "github.com/sagernet/sing-box/common/tls" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" @@ -94,6 +95,10 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog return nil, E.Cause(err, "create server transport: ", options.Transport.Type) } } + inbound.router, err = mux.NewRouterWithOptions(inbound.router, logger, common.PtrValueOrDefault(options.Multiplex)) + if err != nil { + return nil, err + } inbound.service = service inbound.connHandler = inbound return inbound, nil diff --git a/inbound/tuic.go b/inbound/tuic.go index bdef1c5deb..ff9b9ce728 100644 --- a/inbound/tuic.go +++ b/inbound/tuic.go @@ -9,6 +9,7 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/tls" + "github.com/sagernet/sing-box/common/uot" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" @@ -44,7 +45,7 @@ func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogge protocol: C.TypeTUIC, network: []string{N.NetworkUDP}, ctx: ctx, - router: router, + router: uot.NewRouter(router, logger), logger: logger, tag: tag, listenOptions: options.ListenOptions, diff --git a/inbound/vless.go b/inbound/vless.go index 11df86bfed..029fdaf756 100644 --- a/inbound/vless.go +++ b/inbound/vless.go @@ -6,7 +6,9 @@ import ( "os" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/mux" "github.com/sagernet/sing-box/common/tls" + "github.com/sagernet/sing-box/common/uot" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" @@ -42,7 +44,7 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg protocol: C.TypeVLESS, network: []string{N.NetworkTCP}, ctx: ctx, - router: router, + router: uot.NewRouter(router, logger), logger: logger, tag: tag, listenOptions: options.ListenOptions, @@ -50,6 +52,11 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg ctx: ctx, users: options.Users, } + var err error + inbound.router, err = mux.NewRouterWithOptions(inbound.router, logger, common.PtrValueOrDefault(options.Multiplex)) + if err != nil { + return nil, err + } service := vless.NewService[int](logger, adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound)) service.UpdateUsers(common.MapIndexed(inbound.users, func(index int, _ option.VLESSUser) int { return index @@ -59,7 +66,6 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg return it.Flow })) inbound.service = service - var err error if options.TLS != nil { inbound.tlsConfig, err = tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS)) if err != nil { diff --git a/inbound/vmess.go b/inbound/vmess.go index 77a8b28acf..70676bbd8c 100644 --- a/inbound/vmess.go +++ b/inbound/vmess.go @@ -6,7 +6,9 @@ import ( "os" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/mux" "github.com/sagernet/sing-box/common/tls" + "github.com/sagernet/sing-box/common/uot" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" @@ -42,7 +44,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg protocol: C.TypeVMess, network: []string{N.NetworkTCP}, ctx: ctx, - router: router, + router: uot.NewRouter(router, logger), logger: logger, tag: tag, listenOptions: options.ListenOptions, @@ -50,6 +52,11 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg ctx: ctx, users: options.Users, } + var err error + inbound.router, err = mux.NewRouterWithOptions(inbound.router, logger, common.PtrValueOrDefault(options.Multiplex)) + if err != nil { + return nil, err + } var serviceOptions []vmess.ServiceOption if timeFunc := ntp.TimeFuncFromContext(ctx); timeFunc != nil { serviceOptions = append(serviceOptions, vmess.ServiceWithTimeFunc(timeFunc)) @@ -59,7 +66,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg } service := vmess.NewService[int](adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound), serviceOptions...) inbound.service = service - err := service.UpdateUsers(common.MapIndexed(options.Users, func(index int, it option.VMessUser) int { + err = service.UpdateUsers(common.MapIndexed(options.Users, func(index int, it option.VMessUser) int { return index }), common.Map(options.Users, func(it option.VMessUser) string { return it.UUID diff --git a/mkdocs.yml b/mkdocs.yml index 5632affae6..98c46c6fb9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -75,6 +75,7 @@ nav: - Multiplex: configuration/shared/multiplex.md - V2Ray Transport: configuration/shared/v2ray-transport.md - UDP over TCP: configuration/shared/udp-over-tcp.md + - TCP Brutal: configuration/shared/tcp-brutal.md - Inbound: - configuration/inbound/index.md - Direct: configuration/inbound/direct.md diff --git a/option/multiplex.go b/option/multiplex.go new file mode 100644 index 0000000000..309d8bdc39 --- /dev/null +++ b/option/multiplex.go @@ -0,0 +1,23 @@ +package option + +type InboundMultiplexOptions struct { + Enabled bool `json:"enabled,omitempty"` + Padding bool `json:"padding,omitempty"` + Brutal *BrutalOptions `json:"brutal,omitempty"` +} + +type OutboundMultiplexOptions struct { + Enabled bool `json:"enabled,omitempty"` + Protocol string `json:"protocol,omitempty"` + MaxConnections int `json:"max_connections,omitempty"` + MinStreams int `json:"min_streams,omitempty"` + MaxStreams int `json:"max_streams,omitempty"` + Padding bool `json:"padding,omitempty"` + Brutal *BrutalOptions `json:"brutal,omitempty"` +} + +type BrutalOptions struct { + Enabled bool `json:"enabled,omitempty"` + UpMbps int `json:"up_mbps,omitempty"` + DownMbps int `json:"down_mbps,omitempty"` +} diff --git a/option/outbound.go b/option/outbound.go index 3d780ef4ed..2985319ea5 100644 --- a/option/outbound.go +++ b/option/outbound.go @@ -154,12 +154,3 @@ type ServerOptions struct { func (o ServerOptions) Build() M.Socksaddr { return M.ParseSocksaddrHostPort(o.Server, o.ServerPort) } - -type MultiplexOptions struct { - Enabled bool `json:"enabled,omitempty"` - Protocol string `json:"protocol,omitempty"` - MaxConnections int `json:"max_connections,omitempty"` - MinStreams int `json:"min_streams,omitempty"` - MaxStreams int `json:"max_streams,omitempty"` - Padding bool `json:"padding,omitempty"` -} diff --git a/option/shadowsocks.go b/option/shadowsocks.go index 38a18b832e..187b9b633d 100644 --- a/option/shadowsocks.go +++ b/option/shadowsocks.go @@ -7,6 +7,7 @@ type ShadowsocksInboundOptions struct { Password string `json:"password,omitempty"` Users []ShadowsocksUser `json:"users,omitempty"` Destinations []ShadowsocksDestination `json:"destinations,omitempty"` + Multiplex *InboundMultiplexOptions `json:"multiplex,omitempty"` } type ShadowsocksUser struct { @@ -23,11 +24,11 @@ type ShadowsocksDestination struct { type ShadowsocksOutboundOptions struct { DialerOptions ServerOptions - Method string `json:"method"` - Password string `json:"password"` - Plugin string `json:"plugin,omitempty"` - PluginOptions string `json:"plugin_opts,omitempty"` - Network NetworkList `json:"network,omitempty"` - UDPOverTCPOptions *UDPOverTCPOptions `json:"udp_over_tcp,omitempty"` - MultiplexOptions *MultiplexOptions `json:"multiplex,omitempty"` + Method string `json:"method"` + Password string `json:"password"` + Plugin string `json:"plugin,omitempty"` + PluginOptions string `json:"plugin_opts,omitempty"` + Network NetworkList `json:"network,omitempty"` + UDPOverTCP *UDPOverTCPOptions `json:"udp_over_tcp,omitempty"` + Multiplex *OutboundMultiplexOptions `json:"multiplex,omitempty"` } diff --git a/option/simple.go b/option/simple.go index 55bfe93005..b8692e0cf1 100644 --- a/option/simple.go +++ b/option/simple.go @@ -17,11 +17,11 @@ type HTTPMixedInboundOptions struct { type SocksOutboundOptions struct { DialerOptions ServerOptions - Version string `json:"version,omitempty"` - Username string `json:"username,omitempty"` - Password string `json:"password,omitempty"` - Network NetworkList `json:"network,omitempty"` - UDPOverTCPOptions *UDPOverTCPOptions `json:"udp_over_tcp,omitempty"` + Version string `json:"version,omitempty"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Network NetworkList `json:"network,omitempty"` + UDPOverTCP *UDPOverTCPOptions `json:"udp_over_tcp,omitempty"` } type HTTPOutboundOptions struct { diff --git a/option/trojan.go b/option/trojan.go index 11392cd501..d24ed16aed 100644 --- a/option/trojan.go +++ b/option/trojan.go @@ -6,6 +6,7 @@ type TrojanInboundOptions struct { TLS *InboundTLSOptions `json:"tls,omitempty"` Fallback *ServerOptions `json:"fallback,omitempty"` FallbackForALPN map[string]*ServerOptions `json:"fallback_for_alpn,omitempty"` + Multiplex *InboundMultiplexOptions `json:"multiplex,omitempty"` Transport *V2RayTransportOptions `json:"transport,omitempty"` } @@ -17,9 +18,9 @@ type TrojanUser struct { type TrojanOutboundOptions struct { DialerOptions ServerOptions - Password string `json:"password"` - Network NetworkList `json:"network,omitempty"` - TLS *OutboundTLSOptions `json:"tls,omitempty"` - Multiplex *MultiplexOptions `json:"multiplex,omitempty"` - Transport *V2RayTransportOptions `json:"transport,omitempty"` + Password string `json:"password"` + Network NetworkList `json:"network,omitempty"` + TLS *OutboundTLSOptions `json:"tls,omitempty"` + Multiplex *OutboundMultiplexOptions `json:"multiplex,omitempty"` + Transport *V2RayTransportOptions `json:"transport,omitempty"` } diff --git a/option/vless.go b/option/vless.go index 5547bd88c7..09010922e9 100644 --- a/option/vless.go +++ b/option/vless.go @@ -2,9 +2,10 @@ package option type VLESSInboundOptions struct { ListenOptions - Users []VLESSUser `json:"users,omitempty"` - TLS *InboundTLSOptions `json:"tls,omitempty"` - Transport *V2RayTransportOptions `json:"transport,omitempty"` + Users []VLESSUser `json:"users,omitempty"` + TLS *InboundTLSOptions `json:"tls,omitempty"` + Multiplex *InboundMultiplexOptions `json:"multiplex,omitempty"` + Transport *V2RayTransportOptions `json:"transport,omitempty"` } type VLESSUser struct { @@ -16,11 +17,11 @@ type VLESSUser struct { type VLESSOutboundOptions struct { DialerOptions ServerOptions - UUID string `json:"uuid"` - Flow string `json:"flow,omitempty"` - Network NetworkList `json:"network,omitempty"` - TLS *OutboundTLSOptions `json:"tls,omitempty"` - Multiplex *MultiplexOptions `json:"multiplex,omitempty"` - Transport *V2RayTransportOptions `json:"transport,omitempty"` - PacketEncoding *string `json:"packet_encoding,omitempty"` + UUID string `json:"uuid"` + Flow string `json:"flow,omitempty"` + Network NetworkList `json:"network,omitempty"` + TLS *OutboundTLSOptions `json:"tls,omitempty"` + Multiplex *OutboundMultiplexOptions `json:"multiplex,omitempty"` + Transport *V2RayTransportOptions `json:"transport,omitempty"` + PacketEncoding *string `json:"packet_encoding,omitempty"` } diff --git a/option/vmess.go b/option/vmess.go index a2ba210315..8045e9d6da 100644 --- a/option/vmess.go +++ b/option/vmess.go @@ -2,9 +2,10 @@ package option type VMessInboundOptions struct { ListenOptions - Users []VMessUser `json:"users,omitempty"` - TLS *InboundTLSOptions `json:"tls,omitempty"` - Transport *V2RayTransportOptions `json:"transport,omitempty"` + Users []VMessUser `json:"users,omitempty"` + TLS *InboundTLSOptions `json:"tls,omitempty"` + Multiplex *InboundMultiplexOptions `json:"multiplex,omitempty"` + Transport *V2RayTransportOptions `json:"transport,omitempty"` } type VMessUser struct { @@ -16,14 +17,14 @@ type VMessUser struct { type VMessOutboundOptions struct { DialerOptions ServerOptions - UUID string `json:"uuid"` - Security string `json:"security"` - AlterId int `json:"alter_id,omitempty"` - GlobalPadding bool `json:"global_padding,omitempty"` - AuthenticatedLength bool `json:"authenticated_length,omitempty"` - Network NetworkList `json:"network,omitempty"` - TLS *OutboundTLSOptions `json:"tls,omitempty"` - PacketEncoding string `json:"packet_encoding,omitempty"` - Multiplex *MultiplexOptions `json:"multiplex,omitempty"` - Transport *V2RayTransportOptions `json:"transport,omitempty"` + UUID string `json:"uuid"` + Security string `json:"security"` + AlterId int `json:"alter_id,omitempty"` + GlobalPadding bool `json:"global_padding,omitempty"` + AuthenticatedLength bool `json:"authenticated_length,omitempty"` + Network NetworkList `json:"network,omitempty"` + TLS *OutboundTLSOptions `json:"tls,omitempty"` + PacketEncoding string `json:"packet_encoding,omitempty"` + Multiplex *OutboundMultiplexOptions `json:"multiplex,omitempty"` + Transport *V2RayTransportOptions `json:"transport,omitempty"` } diff --git a/outbound/shadowsocks.go b/outbound/shadowsocks.go index e436e1241d..addb6e576f 100644 --- a/outbound/shadowsocks.go +++ b/outbound/shadowsocks.go @@ -62,9 +62,9 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte return nil, err } } - uotOptions := common.PtrValueOrDefault(options.UDPOverTCPOptions) + uotOptions := common.PtrValueOrDefault(options.UDPOverTCP) if !uotOptions.Enabled { - outbound.multiplexDialer, err = mux.NewClientWithOptions((*shadowsocksDialer)(outbound), common.PtrValueOrDefault(options.MultiplexOptions)) + outbound.multiplexDialer, err = mux.NewClientWithOptions((*shadowsocksDialer)(outbound), logger, common.PtrValueOrDefault(options.Multiplex)) if err != nil { return nil, err } diff --git a/outbound/socks.go b/outbound/socks.go index bd6b1ada73..063f7b952a 100644 --- a/outbound/socks.go +++ b/outbound/socks.go @@ -54,7 +54,7 @@ func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, optio client: socks.NewClient(outboundDialer, options.ServerOptions.Build(), version, options.Username, options.Password), resolve: version == socks.Version4, } - uotOptions := common.PtrValueOrDefault(options.UDPOverTCPOptions) + uotOptions := common.PtrValueOrDefault(options.UDPOverTCP) if uotOptions.Enabled { outbound.uotClient = &uot.Client{ Dialer: outbound.client, diff --git a/outbound/trojan.go b/outbound/trojan.go index e8ccb6ae9d..1436961394 100644 --- a/outbound/trojan.go +++ b/outbound/trojan.go @@ -62,7 +62,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog return nil, E.Cause(err, "create client transport: ", options.Transport.Type) } } - outbound.multiplexDialer, err = mux.NewClientWithOptions((*trojanDialer)(outbound), common.PtrValueOrDefault(options.Multiplex)) + outbound.multiplexDialer, err = mux.NewClientWithOptions((*trojanDialer)(outbound), logger, common.PtrValueOrDefault(options.Multiplex)) if err != nil { return nil, err } diff --git a/outbound/vless.go b/outbound/vless.go index a78bddcd2e..506521f700 100644 --- a/outbound/vless.go +++ b/outbound/vless.go @@ -81,7 +81,7 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg if err != nil { return nil, err } - outbound.multiplexDialer, err = mux.NewClientWithOptions((*vlessDialer)(outbound), common.PtrValueOrDefault(options.Multiplex)) + outbound.multiplexDialer, err = mux.NewClientWithOptions((*vlessDialer)(outbound), logger, common.PtrValueOrDefault(options.Multiplex)) if err != nil { return nil, err } diff --git a/outbound/vmess.go b/outbound/vmess.go index a981464bbd..6add541481 100644 --- a/outbound/vmess.go +++ b/outbound/vmess.go @@ -64,7 +64,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg return nil, E.Cause(err, "create client transport: ", options.Transport.Type) } } - outbound.multiplexDialer, err = mux.NewClientWithOptions((*vmessDialer)(outbound), common.PtrValueOrDefault(options.Multiplex)) + outbound.multiplexDialer, err = mux.NewClientWithOptions((*vmessDialer)(outbound), logger, common.PtrValueOrDefault(options.Multiplex)) if err != nil { return nil, err } diff --git a/route/router.go b/route/router.go index 676ebd7b20..2d2f2cde57 100644 --- a/route/router.go +++ b/route/router.go @@ -16,7 +16,6 @@ import ( "github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/geoip" "github.com/sagernet/sing-box/common/geosite" - "github.com/sagernet/sing-box/common/mux" "github.com/sagernet/sing-box/common/process" "github.com/sagernet/sing-box/common/sniff" C "github.com/sagernet/sing-box/constant" @@ -27,6 +26,7 @@ import ( "github.com/sagernet/sing-box/outbound" "github.com/sagernet/sing-box/transport/fakeip" "github.com/sagernet/sing-dns" + mux "github.com/sagernet/sing-mux" "github.com/sagernet/sing-tun" "github.com/sagernet/sing-vmess" "github.com/sagernet/sing/common" @@ -606,30 +606,13 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad metadata.Network = N.NetworkTCP switch metadata.Destination.Fqdn { case mux.Destination.Fqdn: - r.logger.InfoContext(ctx, "inbound multiplex connection") - handler := adapter.NewUpstreamHandler(metadata, r.RouteConnection, r.RoutePacketConnection, r) - return mux.HandleConnection(ctx, handler, r.logger, conn, adapter.UpstreamMetadata(metadata)) + return E.New("global multiplex is deprecated since sing-box v1.7.0, enable multiplex in inbound options instead.") case vmess.MuxDestination.Fqdn: - r.logger.InfoContext(ctx, "inbound legacy multiplex connection") - return vmess.HandleMuxConnection(ctx, conn, adapter.NewUpstreamHandler(metadata, r.RouteConnection, r.RoutePacketConnection, r)) + return E.New("global multiplex (v2ray legacy) not supported since sing-box v1.7.0.") case uot.MagicAddress: - request, err := uot.ReadRequest(conn) - if err != nil { - return E.Cause(err, "read UoT request") - } - if request.IsConnect { - r.logger.InfoContext(ctx, "inbound UoT connect connection to ", request.Destination) - } else { - r.logger.InfoContext(ctx, "inbound UoT connection to ", request.Destination) - } - metadata.Domain = metadata.Destination.Fqdn - metadata.Destination = request.Destination - return r.RoutePacketConnection(ctx, uot.NewConn(conn, *request), metadata) + return E.New("global UoT not supported since sing-box v1.7.0.") case uot.LegacyMagicAddress: - r.logger.InfoContext(ctx, "inbound legacy UoT connection") - metadata.Domain = metadata.Destination.Fqdn - metadata.Destination = M.Socksaddr{Addr: netip.IPv4Unspecified()} - return r.RoutePacketConnection(ctx, uot.NewConn(conn, uot.Request{}), metadata) + return E.New("global UoT (legacy) not supported since sing-box v1.7.0.") } if r.fakeIPStore != nil && r.fakeIPStore.Contains(metadata.Destination.Addr) { diff --git a/test/brutal_test.go b/test/brutal_test.go new file mode 100644 index 0000000000..6bfdfcc1b1 --- /dev/null +++ b/test/brutal_test.go @@ -0,0 +1,346 @@ +package main + +import ( + "net/netip" + "testing" + + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing-shadowsocks/shadowaead_2022" + + "github.com/gofrs/uuid/v5" +) + +func TestBrutalShadowsocks(t *testing.T) { + method := shadowaead_2022.List[0] + password := mkBase64(t, 16) + startInstance(t, option.Options{ + Inbounds: []option.Inbound{ + { + Type: C.TypeMixed, + Tag: "mixed-in", + MixedOptions: option.HTTPMixedInboundOptions{ + ListenOptions: option.ListenOptions{ + Listen: option.NewListenAddress(netip.IPv4Unspecified()), + ListenPort: clientPort, + }, + }, + }, + { + Type: C.TypeShadowsocks, + ShadowsocksOptions: option.ShadowsocksInboundOptions{ + ListenOptions: option.ListenOptions{ + Listen: option.NewListenAddress(netip.IPv4Unspecified()), + ListenPort: serverPort, + }, + Method: method, + Password: password, + Multiplex: &option.InboundMultiplexOptions{ + Enabled: true, + Brutal: &option.BrutalOptions{ + Enabled: true, + UpMbps: 100, + DownMbps: 100, + }, + }, + }, + }, + }, + Outbounds: []option.Outbound{ + { + Type: C.TypeDirect, + }, + { + Type: C.TypeShadowsocks, + Tag: "ss-out", + ShadowsocksOptions: option.ShadowsocksOutboundOptions{ + ServerOptions: option.ServerOptions{ + Server: "127.0.0.1", + ServerPort: serverPort, + }, + Method: method, + Password: password, + Multiplex: &option.OutboundMultiplexOptions{ + Enabled: true, + Protocol: "smux", + Padding: true, + Brutal: &option.BrutalOptions{ + Enabled: true, + UpMbps: 100, + DownMbps: 100, + }, + }, + }, + }, + }, + Route: &option.RouteOptions{ + Rules: []option.Rule{ + { + DefaultOptions: option.DefaultRule{ + Inbound: []string{"mixed-in"}, + Outbound: "ss-out", + }, + }, + }, + }, + }) + testSuit(t, clientPort, testPort) +} + +func TestBrutalTrojan(t *testing.T) { + _, certPem, keyPem := createSelfSignedCertificate(t, "example.org") + password := mkBase64(t, 16) + startInstance(t, option.Options{ + Inbounds: []option.Inbound{ + { + Type: C.TypeMixed, + Tag: "mixed-in", + MixedOptions: option.HTTPMixedInboundOptions{ + ListenOptions: option.ListenOptions{ + Listen: option.NewListenAddress(netip.IPv4Unspecified()), + ListenPort: clientPort, + }, + }, + }, + { + Type: C.TypeTrojan, + TrojanOptions: option.TrojanInboundOptions{ + ListenOptions: option.ListenOptions{ + Listen: option.NewListenAddress(netip.IPv4Unspecified()), + ListenPort: serverPort, + }, + Users: []option.TrojanUser{{Password: password}}, + Multiplex: &option.InboundMultiplexOptions{ + Enabled: true, + Brutal: &option.BrutalOptions{ + Enabled: true, + UpMbps: 100, + DownMbps: 100, + }, + }, + TLS: &option.InboundTLSOptions{ + Enabled: true, + ServerName: "example.org", + CertificatePath: certPem, + KeyPath: keyPem, + }, + }, + }, + }, + Outbounds: []option.Outbound{ + { + Type: C.TypeDirect, + }, + { + Type: C.TypeTrojan, + Tag: "ss-out", + TrojanOptions: option.TrojanOutboundOptions{ + ServerOptions: option.ServerOptions{ + Server: "127.0.0.1", + ServerPort: serverPort, + }, + Password: password, + Multiplex: &option.OutboundMultiplexOptions{ + Enabled: true, + Protocol: "yamux", + Padding: true, + Brutal: &option.BrutalOptions{ + Enabled: true, + UpMbps: 100, + DownMbps: 100, + }, + }, + TLS: &option.OutboundTLSOptions{ + Enabled: true, + ServerName: "example.org", + CertificatePath: certPem, + }, + }, + }, + }, + Route: &option.RouteOptions{ + Rules: []option.Rule{ + { + DefaultOptions: option.DefaultRule{ + Inbound: []string{"mixed-in"}, + Outbound: "ss-out", + }, + }, + }, + }, + }) + testSuit(t, clientPort, testPort) +} + +func TestBrutalVMess(t *testing.T) { + user, _ := uuid.NewV4() + startInstance(t, option.Options{ + Inbounds: []option.Inbound{ + { + Type: C.TypeMixed, + Tag: "mixed-in", + MixedOptions: option.HTTPMixedInboundOptions{ + ListenOptions: option.ListenOptions{ + Listen: option.NewListenAddress(netip.IPv4Unspecified()), + ListenPort: clientPort, + }, + }, + }, + { + Type: C.TypeVMess, + VMessOptions: option.VMessInboundOptions{ + ListenOptions: option.ListenOptions{ + Listen: option.NewListenAddress(netip.IPv4Unspecified()), + ListenPort: serverPort, + }, + Users: []option.VMessUser{{UUID: user.String()}}, + Multiplex: &option.InboundMultiplexOptions{ + Enabled: true, + Brutal: &option.BrutalOptions{ + Enabled: true, + UpMbps: 100, + DownMbps: 100, + }, + }, + }, + }, + }, + Outbounds: []option.Outbound{ + { + Type: C.TypeDirect, + }, + { + Type: C.TypeVMess, + Tag: "ss-out", + VMessOptions: option.VMessOutboundOptions{ + ServerOptions: option.ServerOptions{ + Server: "127.0.0.1", + ServerPort: serverPort, + }, + UUID: user.String(), + Multiplex: &option.OutboundMultiplexOptions{ + Enabled: true, + Protocol: "h2mux", + Padding: true, + Brutal: &option.BrutalOptions{ + Enabled: true, + UpMbps: 100, + DownMbps: 100, + }, + }, + }, + }, + }, + Route: &option.RouteOptions{ + Rules: []option.Rule{ + { + DefaultOptions: option.DefaultRule{ + Inbound: []string{"mixed-in"}, + Outbound: "ss-out", + }, + }, + }, + }, + }) + testSuit(t, clientPort, testPort) +} + +func TestBrutalVLESS(t *testing.T) { + user, _ := uuid.NewV4() + startInstance(t, option.Options{ + Inbounds: []option.Inbound{ + { + Type: C.TypeMixed, + Tag: "mixed-in", + MixedOptions: option.HTTPMixedInboundOptions{ + ListenOptions: option.ListenOptions{ + Listen: option.NewListenAddress(netip.IPv4Unspecified()), + ListenPort: clientPort, + }, + }, + }, + { + Type: C.TypeVLESS, + VLESSOptions: option.VLESSInboundOptions{ + ListenOptions: option.ListenOptions{ + Listen: option.NewListenAddress(netip.IPv4Unspecified()), + ListenPort: serverPort, + }, + Users: []option.VLESSUser{{UUID: user.String()}}, + Multiplex: &option.InboundMultiplexOptions{ + Enabled: true, + Brutal: &option.BrutalOptions{ + Enabled: true, + UpMbps: 100, + DownMbps: 100, + }, + }, + TLS: &option.InboundTLSOptions{ + Enabled: true, + ServerName: "google.com", + Reality: &option.InboundRealityOptions{ + Enabled: true, + Handshake: option.InboundRealityHandshakeOptions{ + ServerOptions: option.ServerOptions{ + Server: "google.com", + ServerPort: 443, + }, + }, + ShortID: []string{"0123456789abcdef"}, + PrivateKey: "UuMBgl7MXTPx9inmQp2UC7Jcnwc6XYbwDNebonM-FCc", + }, + }, + }, + }, + }, + Outbounds: []option.Outbound{ + { + Type: C.TypeDirect, + }, + { + Type: C.TypeVLESS, + Tag: "ss-out", + VLESSOptions: option.VLESSOutboundOptions{ + ServerOptions: option.ServerOptions{ + Server: "127.0.0.1", + ServerPort: serverPort, + }, + UUID: user.String(), + TLS: &option.OutboundTLSOptions{ + Enabled: true, + ServerName: "google.com", + Reality: &option.OutboundRealityOptions{ + Enabled: true, + ShortID: "0123456789abcdef", + PublicKey: "jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0", + }, + UTLS: &option.OutboundUTLSOptions{ + Enabled: true, + }, + }, + Multiplex: &option.OutboundMultiplexOptions{ + Enabled: true, + Protocol: "h2mux", + Padding: true, + Brutal: &option.BrutalOptions{ + Enabled: true, + UpMbps: 100, + DownMbps: 100, + }, + }, + }, + }, + }, + Route: &option.RouteOptions{ + Rules: []option.Rule{ + { + DefaultOptions: option.DefaultRule{ + Inbound: []string{"mixed-in"}, + Outbound: "ss-out", + }, + }, + }, + }, + }) + testSuit(t, clientPort, testPort) +} diff --git a/test/go.mod b/test/go.mod index 108a09608b..40ceb54ffc 100644 --- a/test/go.mod +++ b/test/go.mod @@ -75,7 +75,7 @@ require ( github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect - github.com/sagernet/sing-mux v0.1.4 // indirect + github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 // indirect github.com/sagernet/sing-shadowtls v0.1.4 // indirect github.com/sagernet/sing-tun v0.1.20-0.20231116102736-3fa4ee409a9d // indirect github.com/sagernet/sing-vmess v0.1.8 // indirect diff --git a/test/go.sum b/test/go.sum index b4a07c6cb8..53a7698337 100644 --- a/test/go.sum +++ b/test/go.sum @@ -135,8 +135,8 @@ github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c h1:uask61Pxc3nGqs github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358 h1:psJQg/KXVQTupFFR1liaCAucW+NwCDhe1oYfkmz8EJ8= github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358/go.mod h1:vtUimtf7Nq9EdvD5WTpfCr69KL1M7bcgOVKiYBiAY/c= -github.com/sagernet/sing-mux v0.1.4 h1:BPNPOQr6HkXG3iY/BrfvUKUl+A7gYsGKVSxvoR3PO50= -github.com/sagernet/sing-mux v0.1.4/go.mod h1:dKvcu/sb3fZ88uGv9vzAqUej6J4W+pHu5GqjRuFwAWs= +github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 h1:ncKb5tVOsCQgCsv6UpsA0jinbNb5OQ5GMPJlyQP3EHM= +github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07/go.mod h1:u/MZf32xPG8jEKe3t+xUV67EBnKtDtCaPhsJQOQGUYU= github.com/sagernet/sing-quic v0.1.4-0.20231111111624-370e6abf6769 h1:V84NPJKirL/oDkvUh9Dor2gMZ+TTNHK3jcedqRkgcQA= github.com/sagernet/sing-quic v0.1.4-0.20231111111624-370e6abf6769/go.mod h1:iggBfFE2ojS2yYQU8Hnrkm029RsHPdYYIKWqeCI64HE= github.com/sagernet/sing-shadowsocks v0.2.5 h1:qxIttos4xu6ii7MTVJYA8EFQR7Q3KG6xMqmLJIFtBaY= diff --git a/test/mux_test.go b/test/mux_test.go index b57c6cee99..564b936d8e 100644 --- a/test/mux_test.go +++ b/test/mux_test.go @@ -18,7 +18,7 @@ var muxProtocols = []string{ } func TestVMessSMux(t *testing.T) { - testVMessMux(t, option.MultiplexOptions{ + testVMessMux(t, option.OutboundMultiplexOptions{ Enabled: true, Protocol: "smux", }) @@ -27,7 +27,7 @@ func TestVMessSMux(t *testing.T) { func TestShadowsocksMux(t *testing.T) { for _, protocol := range muxProtocols { t.Run(protocol, func(t *testing.T) { - testShadowsocksMux(t, option.MultiplexOptions{ + testShadowsocksMux(t, option.OutboundMultiplexOptions{ Enabled: true, Protocol: protocol, }) @@ -36,7 +36,7 @@ func TestShadowsocksMux(t *testing.T) { } func TestShadowsockH2Mux(t *testing.T) { - testShadowsocksMux(t, option.MultiplexOptions{ + testShadowsocksMux(t, option.OutboundMultiplexOptions{ Enabled: true, Protocol: "h2mux", Padding: true, @@ -44,14 +44,14 @@ func TestShadowsockH2Mux(t *testing.T) { } func TestShadowsockSMuxPadding(t *testing.T) { - testShadowsocksMux(t, option.MultiplexOptions{ + testShadowsocksMux(t, option.OutboundMultiplexOptions{ Enabled: true, Protocol: "smux", Padding: true, }) } -func testShadowsocksMux(t *testing.T, options option.MultiplexOptions) { +func testShadowsocksMux(t *testing.T, options option.OutboundMultiplexOptions) { method := shadowaead_2022.List[0] password := mkBase64(t, 16) startInstance(t, option.Options{ @@ -90,9 +90,9 @@ func testShadowsocksMux(t *testing.T, options option.MultiplexOptions) { Server: "127.0.0.1", ServerPort: serverPort, }, - Method: method, - Password: password, - MultiplexOptions: &options, + Method: method, + Password: password, + Multiplex: &options, }, }, }, @@ -110,7 +110,7 @@ func testShadowsocksMux(t *testing.T, options option.MultiplexOptions) { testSuit(t, clientPort, testPort) } -func testVMessMux(t *testing.T, options option.MultiplexOptions) { +func testVMessMux(t *testing.T, options option.OutboundMultiplexOptions) { user, _ := uuid.NewV4() startInstance(t, option.Options{ Inbounds: []option.Inbound{ @@ -136,6 +136,9 @@ func testVMessMux(t *testing.T, options option.MultiplexOptions) { UUID: user.String(), }, }, + Multiplex: &option.InboundMultiplexOptions{ + Enabled: true, + }, }, }, }, diff --git a/test/shadowsocks_test.go b/test/shadowsocks_test.go index 07b324d214..f88715861a 100644 --- a/test/shadowsocks_test.go +++ b/test/shadowsocks_test.go @@ -232,7 +232,7 @@ func TestShadowsocksUoT(t *testing.T) { }, Method: method, Password: password, - UDPOverTCPOptions: &option.UDPOverTCPOptions{ + UDPOverTCP: &option.UDPOverTCPOptions{ Enabled: true, }, }, From 96604c568135b792372b84c2dcf08cd77c86ed58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 14 Nov 2023 21:48:55 +0800 Subject: [PATCH 06/11] Update quic-go to v0.40.0 --- go.mod | 16 ++++++++-------- go.sum | 41 ++++++++++++++++++----------------------- 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/go.mod b/go.mod index a6b9461c53..f6f15c3d00 100644 --- a/go.mod +++ b/go.mod @@ -24,12 +24,12 @@ require ( github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950 github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab - github.com/sagernet/quic-go v0.0.0-20231008035953-32727fef9460 + github.com/sagernet/quic-go v0.40.0 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358 github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 - github.com/sagernet/sing-quic v0.1.4-0.20231111111624-370e6abf6769 + github.com/sagernet/sing-quic v0.1.4-0.20231114135334-e2a6aab55cca github.com/sagernet/sing-shadowsocks v0.2.5 github.com/sagernet/sing-shadowsocks2 v0.1.4 github.com/sagernet/sing-shadowtls v0.1.4 @@ -65,7 +65,7 @@ require ( github.com/gobwas/pool v0.2.1 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/btree v1.1.2 // indirect - github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect + github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/native v1.1.0 // indirect @@ -73,11 +73,11 @@ require ( github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/libdns/libdns v0.2.1 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect - github.com/onsi/ginkgo/v2 v2.9.5 // indirect + github.com/onsi/ginkgo/v2 v2.9.7 // indirect github.com/pierrec/lz4/v4 v4.1.14 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-20 v0.3.4 // indirect + github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect @@ -86,11 +86,11 @@ require ( github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect github.com/zeebo/blake3 v0.2.3 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect - golang.org/x/mod v0.13.0 // indirect + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + golang.org/x/mod v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.14.0 // indirect + golang.org/x/tools v0.15.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index df3241c4e5..46022c9d0b 100644 --- a/go.sum +++ b/go.sum @@ -6,9 +6,6 @@ github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/ github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/caddyserver/certmagic v0.19.2 h1:HZd1AKLx4592MalEGQS39DKs2ZOAJCEM/xYPMQ2/ui0= github.com/caddyserver/certmagic v0.19.2/go.mod h1:fsL01NomQ6N+kE2j37ZCnig2MFosG+MIO4ztnmG/zz8= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cloudflare/circl v1.3.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg= github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -44,11 +41,10 @@ github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk= +github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c h1:PgxFEySCI41sH0mB7/2XswdXbUykQsRUGod8Rn+NubM= @@ -80,9 +76,9 @@ github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= -github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= -github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= +github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= +github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= github.com/ooni/go-libtor v1.1.8 h1:Wo3V3DVTxl5vZdxtQakqYP+DAHx7pPtAFSl1bnAa08w= github.com/ooni/go-libtor v1.1.8/go.mod h1:q1YyLwRD9GeMyeerVvwc0vJ2YgwDLTp2bdVcrh/JXyI= github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs= @@ -93,8 +89,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg= -github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= +github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM= @@ -108,8 +104,8 @@ github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab h1:u+xQoi/Yc6bNUvT github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab/go.mod h1:3akUhSHSVtLuJaYcW5JPepUraBOW06Ibz2HKwaK5rOk= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= -github.com/sagernet/quic-go v0.0.0-20231008035953-32727fef9460 h1:dAe4OIJAtE0nHOzTHhAReQteh3+sa63rvXbuIpbeOTY= -github.com/sagernet/quic-go v0.0.0-20231008035953-32727fef9460/go.mod h1:uJGpmJCOcMQqMlHKc3P1Vz6uygmpz4bPeVIoOhdVQnM= +github.com/sagernet/quic-go v0.40.0 h1:DvQNPb72lzvNQDe9tcUyHTw8eRv6PLtM2mNYmdlzUMo= +github.com/sagernet/quic-go v0.40.0/go.mod h1:VqtdhlbkeeG5Okhb3eDMb/9o0EoglReHunNT9ukrJAI= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= @@ -120,8 +116,8 @@ github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358 h1:psJQg/KXVQ github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358/go.mod h1:vtUimtf7Nq9EdvD5WTpfCr69KL1M7bcgOVKiYBiAY/c= github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 h1:ncKb5tVOsCQgCsv6UpsA0jinbNb5OQ5GMPJlyQP3EHM= github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07/go.mod h1:u/MZf32xPG8jEKe3t+xUV67EBnKtDtCaPhsJQOQGUYU= -github.com/sagernet/sing-quic v0.1.4-0.20231111111624-370e6abf6769 h1:V84NPJKirL/oDkvUh9Dor2gMZ+TTNHK3jcedqRkgcQA= -github.com/sagernet/sing-quic v0.1.4-0.20231111111624-370e6abf6769/go.mod h1:iggBfFE2ojS2yYQU8Hnrkm029RsHPdYYIKWqeCI64HE= +github.com/sagernet/sing-quic v0.1.4-0.20231114135334-e2a6aab55cca h1:wGQhe7D8Y4D3lPK8Obtv4IQAvnkEKMMu8Uv/BiYpyWc= +github.com/sagernet/sing-quic v0.1.4-0.20231114135334-e2a6aab55cca/go.mod h1:B6OgRz+qLn3N1114dcZVExkdarArtsAX2MgWJIfB72c= github.com/sagernet/sing-shadowsocks v0.2.5 h1:qxIttos4xu6ii7MTVJYA8EFQR7Q3KG6xMqmLJIFtBaY= github.com/sagernet/sing-shadowsocks v0.2.5/go.mod h1:MGWGkcU2xW2G2mfArT9/QqpVLOGU+dBaahZCtPHdt7A= github.com/sagernet/sing-shadowsocks2 v0.1.4 h1:vht2M8t3m5DTgXR2j24KbYOygG5aOp+MUhpQnAux728= @@ -175,17 +171,16 @@ golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaE golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -205,8 +200,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80= From 99e5b7bc280f1476ebfc58f995955e9e82fe1084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sat, 18 Nov 2023 13:51:32 +0800 Subject: [PATCH 07/11] Add `wifi_ssid` and `wifi_bssid` route and DNS rules --- adapter/router.go | 6 ++++ experimental/clashapi/cachefile/cache.go | 8 ++++- experimental/clashapi/server.go | 2 +- experimental/libbox/command_log.go | 2 +- experimental/libbox/platform.go | 10 ++++++ experimental/libbox/platform/interface.go | 1 + experimental/libbox/service.go | 8 +++++ go.mod | 2 +- go.sum | 4 +-- option/rule.go | 2 ++ option/rule_dns.go | 2 ++ route/router.go | 26 +++++++++++++++ route/router_geo_resources.go | 8 +++++ route/rule_default.go | 10 ++++++ route/rule_dns.go | 10 ++++++ route/rule_item_wifi_bssid.go | 39 +++++++++++++++++++++++ route/rule_item_wifi_ssid.go | 39 +++++++++++++++++++++++ 17 files changed, 173 insertions(+), 6 deletions(-) create mode 100644 route/rule_item_wifi_bssid.go create mode 100644 route/rule_item_wifi_ssid.go diff --git a/adapter/router.go b/adapter/router.go index ab2d916c65..e4c3904d51 100644 --- a/adapter/router.go +++ b/adapter/router.go @@ -41,6 +41,7 @@ type Router interface { NetworkMonitor() tun.NetworkUpdateMonitor InterfaceMonitor() tun.DefaultInterfaceMonitor PackageManager() tun.PackageManager + WIFIState() WIFIState Rules() []Rule ClashServer() ClashServer @@ -78,3 +79,8 @@ type DNSRule interface { type InterfaceUpdateListener interface { InterfaceUpdated() } + +type WIFIState struct { + SSID string + BSSID string +} diff --git a/experimental/clashapi/cachefile/cache.go b/experimental/clashapi/cachefile/cache.go index 0b96fa5def..0911829749 100644 --- a/experimental/clashapi/cachefile/cache.go +++ b/experimental/clashapi/cachefile/cache.go @@ -1,6 +1,7 @@ package cachefile import ( + "context" "errors" "net/netip" "os" @@ -13,6 +14,7 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/service/filemanager" ) var ( @@ -41,7 +43,7 @@ type CacheFile struct { saveMetadataTimer *time.Timer } -func Open(path string, cacheID string) (*CacheFile, error) { +func Open(ctx context.Context, path string, cacheID string) (*CacheFile, error) { const fileMode = 0o666 options := bbolt.Options{Timeout: time.Second} var ( @@ -67,6 +69,10 @@ func Open(path string, cacheID string) (*CacheFile, error) { if err != nil { return nil, err } + err = filemanager.Chown(ctx, path) + if err != nil { + return nil, E.Cause(err, "platform chown") + } var cacheIDBytes []byte if cacheID != "" { cacheIDBytes = append([]byte{0}, []byte(cacheID)...) diff --git a/experimental/clashapi/server.go b/experimental/clashapi/server.go index 493d73972b..6a3d6f66f7 100644 --- a/experimental/clashapi/server.go +++ b/experimental/clashapi/server.go @@ -148,7 +148,7 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ func (s *Server) PreStart() error { if s.cacheFilePath != "" { - cacheFile, err := cachefile.Open(s.cacheFilePath, s.cacheID) + cacheFile, err := cachefile.Open(s.ctx, s.cacheFilePath, s.cacheID) if err != nil { return E.Cause(err, "open cache file") } diff --git a/experimental/libbox/command_log.go b/experimental/libbox/command_log.go index ce72010dd0..da142657e6 100644 --- a/experimental/libbox/command_log.go +++ b/experimental/libbox/command_log.go @@ -60,11 +60,11 @@ func (s *CommandServer) handleLogConn(conn net.Conn) error { for element := s.savedLines.Front(); element != nil; element = element.Next() { savedLines = append(savedLines, element.Value) } - s.access.Unlock() subscription, done, err := s.observer.Subscribe() if err != nil { return err } + s.access.Unlock() defer s.observer.UnSubscribe(subscription) for _, line := range savedLines { err = writeLog(conn, []byte(line)) diff --git a/experimental/libbox/platform.go b/experimental/libbox/platform.go index b7418bd279..451a72a9e1 100644 --- a/experimental/libbox/platform.go +++ b/experimental/libbox/platform.go @@ -19,6 +19,7 @@ type PlatformInterface interface { UsePlatformInterfaceGetter() bool GetInterfaces() (NetworkInterfaceIterator, error) UnderNetworkExtension() bool + ReadWIFIState() *WIFIState ClearDNSCache() } @@ -38,6 +39,15 @@ type NetworkInterface struct { Addresses StringIterator } +type WIFIState struct { + SSID string + BSSID string +} + +func NewWIFIState(wifiSSID string, wifiBSSID string) *WIFIState { + return &WIFIState{wifiSSID, wifiBSSID} +} + type NetworkInterfaceIterator interface { Next() *NetworkInterface HasNext() bool diff --git a/experimental/libbox/platform/interface.go b/experimental/libbox/platform/interface.go index 8c47197186..54d35fa315 100644 --- a/experimental/libbox/platform/interface.go +++ b/experimental/libbox/platform/interface.go @@ -23,6 +23,7 @@ type Interface interface { Interfaces() ([]NetworkInterface, error) UnderNetworkExtension() bool ClearDNSCache() + ReadWIFIState() adapter.WIFIState process.Searcher } diff --git a/experimental/libbox/service.go b/experimental/libbox/service.go index ce4d6d2aad..3375f7502c 100644 --- a/experimental/libbox/service.go +++ b/experimental/libbox/service.go @@ -210,6 +210,14 @@ func (w *platformInterfaceWrapper) ClearDNSCache() { w.iif.ClearDNSCache() } +func (w *platformInterfaceWrapper) ReadWIFIState() adapter.WIFIState { + wifiState := w.iif.ReadWIFIState() + if wifiState == nil { + return adapter.WIFIState{} + } + return (adapter.WIFIState)(*wifiState) +} + func (w *platformInterfaceWrapper) DisableColors() bool { return runtime.GOOS != "android" } diff --git a/go.mod b/go.mod index f6f15c3d00..1420aeb624 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab github.com/sagernet/quic-go v0.40.0 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 - github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c + github.com/sagernet/sing v0.2.18-0.20231117150934-256fafcd99b6 github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358 github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 github.com/sagernet/sing-quic v0.1.4-0.20231114135334-e2a6aab55cca diff --git a/go.sum b/go.sum index 46022c9d0b..50f2cdead0 100644 --- a/go.sum +++ b/go.sum @@ -110,8 +110,8 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= -github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c h1:uask61Pxc3nGqsOSjqnBKrwfODWRoEa80lXm04LNk0E= -github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= +github.com/sagernet/sing v0.2.18-0.20231117150934-256fafcd99b6 h1:v6QQn8FPEwF2Wi4jQ0xWg7+NUWMIsnP8uoAG7lua1Qo= +github.com/sagernet/sing v0.2.18-0.20231117150934-256fafcd99b6/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358 h1:psJQg/KXVQTupFFR1liaCAucW+NwCDhe1oYfkmz8EJ8= github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358/go.mod h1:vtUimtf7Nq9EdvD5WTpfCr69KL1M7bcgOVKiYBiAY/c= github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 h1:ncKb5tVOsCQgCsv6UpsA0jinbNb5OQ5GMPJlyQP3EHM= diff --git a/option/rule.go b/option/rule.go index f78a752d91..8caba96e57 100644 --- a/option/rule.go +++ b/option/rule.go @@ -78,6 +78,8 @@ type DefaultRule struct { User Listable[string] `json:"user,omitempty"` UserID Listable[int32] `json:"user_id,omitempty"` ClashMode string `json:"clash_mode,omitempty"` + WIFISSID Listable[string] `json:"wifi_ssid,omitempty"` + WIFIBSSID Listable[string] `json:"wifi_bssid,omitempty"` Invert bool `json:"invert,omitempty"` Outbound string `json:"outbound,omitempty"` } diff --git a/option/rule_dns.go b/option/rule_dns.go index 563d30850b..ba572b9aa2 100644 --- a/option/rule_dns.go +++ b/option/rule_dns.go @@ -78,6 +78,8 @@ type DefaultDNSRule struct { UserID Listable[int32] `json:"user_id,omitempty"` Outbound Listable[string] `json:"outbound,omitempty"` ClashMode string `json:"clash_mode,omitempty"` + WIFISSID Listable[string] `json:"wifi_ssid,omitempty"` + WIFIBSSID Listable[string] `json:"wifi_bssid,omitempty"` Invert bool `json:"invert,omitempty"` Server string `json:"server,omitempty"` DisableCache bool `json:"disable_cache,omitempty"` diff --git a/route/router.go b/route/router.go index 2d2f2cde57..972c4ec048 100644 --- a/route/router.go +++ b/route/router.go @@ -86,6 +86,8 @@ type Router struct { clashServer adapter.ClashServer v2rayServer adapter.V2RayServer platformInterface platform.Interface + needWIFIState bool + wifiState adapter.WIFIState } func NewRouter( @@ -116,6 +118,7 @@ func NewRouter( defaultMark: options.DefaultMark, pauseManager: pause.ManagerFromContext(ctx), platformInterface: platformInterface, + needWIFIState: hasRule(options.Rules, isWIFIRule) || hasDNSRule(dnsOptions.Rules, isWIFIDNSRule), } router.dnsClient = dns.NewClient(dns.ClientOptions{ DisableCache: dnsOptions.DNSClientOptions.DisableCache, @@ -328,6 +331,11 @@ func NewRouter( service.ContextWith[serviceNTP.TimeService](ctx, timeService) router.timeService = timeService } + if platformInterface != nil && router.interfaceMonitor != nil && router.needWIFIState { + router.interfaceMonitor.RegisterCallback(func(_ int) { + router.updateWIFIState() + }) + } return router, nil } @@ -468,6 +476,9 @@ func (r *Router) Start() error { r.geositeCache = nil r.geositeReader = nil } + if r.needWIFIState { + r.updateWIFIState() + } for i, rule := range r.rules { err := rule.Start() if err != nil { @@ -940,6 +951,10 @@ func (r *Router) Rules() []adapter.Rule { return r.rules } +func (r *Router) WIFIState() adapter.WIFIState { + return r.wifiState +} + func (r *Router) NetworkMonitor() tun.NetworkUpdateMonitor { return r.networkMonitor } @@ -1019,3 +1034,14 @@ func (r *Router) ResetNetwork() error { } return nil } + +func (r *Router) updateWIFIState() { + if r.platformInterface == nil { + return + } + state := r.platformInterface.ReadWIFIState() + if state != r.wifiState { + r.wifiState = state + r.logger.Info("updated WIFI state: SSID=", state.SSID, ", BSSID=", state.BSSID) + } +} diff --git a/route/router_geo_resources.go b/route/router_geo_resources.go index 523833dcff..8715cf922c 100644 --- a/route/router_geo_resources.go +++ b/route/router_geo_resources.go @@ -307,3 +307,11 @@ func isProcessDNSRule(rule option.DefaultDNSRule) bool { func notPrivateNode(code string) bool { return code != "private" } + +func isWIFIRule(rule option.DefaultRule) bool { + return len(rule.WIFISSID) > 0 || len(rule.WIFIBSSID) > 0 +} + +func isWIFIDNSRule(rule option.DefaultDNSRule) bool { + return len(rule.WIFISSID) > 0 || len(rule.WIFIBSSID) > 0 +} diff --git a/route/rule_default.go b/route/rule_default.go index 01322c13aa..2d62f97a46 100644 --- a/route/rule_default.go +++ b/route/rule_default.go @@ -184,6 +184,16 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt rule.items = append(rule.items, item) rule.allItems = append(rule.allItems, item) } + if len(options.WIFISSID) > 0 { + item := NewWIFISSIDItem(router, options.WIFISSID) + rule.items = append(rule.items, item) + rule.allItems = append(rule.allItems, item) + } + if len(options.WIFIBSSID) > 0 { + item := NewWIFIBSSIDItem(router, options.WIFIBSSID) + rule.items = append(rule.items, item) + rule.allItems = append(rule.allItems, item) + } return rule, nil } diff --git a/route/rule_dns.go b/route/rule_dns.go index 15e4b16ff1..5132f02456 100644 --- a/route/rule_dns.go +++ b/route/rule_dns.go @@ -180,6 +180,16 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options rule.items = append(rule.items, item) rule.allItems = append(rule.allItems, item) } + if len(options.WIFISSID) > 0 { + item := NewWIFISSIDItem(router, options.WIFISSID) + rule.items = append(rule.items, item) + rule.allItems = append(rule.allItems, item) + } + if len(options.WIFIBSSID) > 0 { + item := NewWIFIBSSIDItem(router, options.WIFIBSSID) + rule.items = append(rule.items, item) + rule.allItems = append(rule.allItems, item) + } return rule, nil } diff --git a/route/rule_item_wifi_bssid.go b/route/rule_item_wifi_bssid.go new file mode 100644 index 0000000000..3b1ff9c852 --- /dev/null +++ b/route/rule_item_wifi_bssid.go @@ -0,0 +1,39 @@ +package route + +import ( + "strings" + + "github.com/sagernet/sing-box/adapter" + F "github.com/sagernet/sing/common/format" +) + +var _ RuleItem = (*WIFIBSSIDItem)(nil) + +type WIFIBSSIDItem struct { + bssidList []string + bssidMap map[string]bool + router adapter.Router +} + +func NewWIFIBSSIDItem(router adapter.Router, bssidList []string) *WIFIBSSIDItem { + bssidMap := make(map[string]bool) + for _, bssid := range bssidList { + bssidMap[bssid] = true + } + return &WIFIBSSIDItem{ + bssidList, + bssidMap, + router, + } +} + +func (r *WIFIBSSIDItem) Match(metadata *adapter.InboundContext) bool { + return r.bssidMap[r.router.WIFIState().BSSID] +} + +func (r *WIFIBSSIDItem) String() string { + if len(r.bssidList) == 1 { + return F.ToString("wifi_bssid=", r.bssidList[0]) + } + return F.ToString("wifi_bssid=[", strings.Join(r.bssidList, " "), "]") +} diff --git a/route/rule_item_wifi_ssid.go b/route/rule_item_wifi_ssid.go new file mode 100644 index 0000000000..62cf935eb0 --- /dev/null +++ b/route/rule_item_wifi_ssid.go @@ -0,0 +1,39 @@ +package route + +import ( + "strings" + + "github.com/sagernet/sing-box/adapter" + F "github.com/sagernet/sing/common/format" +) + +var _ RuleItem = (*WIFISSIDItem)(nil) + +type WIFISSIDItem struct { + ssidList []string + ssidMap map[string]bool + router adapter.Router +} + +func NewWIFISSIDItem(router adapter.Router, ssidList []string) *WIFISSIDItem { + ssidMap := make(map[string]bool) + for _, ssid := range ssidList { + ssidMap[ssid] = true + } + return &WIFISSIDItem{ + ssidList, + ssidMap, + router, + } +} + +func (r *WIFISSIDItem) Match(metadata *adapter.InboundContext) bool { + return r.ssidMap[r.router.WIFIState().SSID] +} + +func (r *WIFISSIDItem) String() string { + if len(r.ssidList) == 1 { + return F.ToString("wifi_ssid=", r.ssidList[0]) + } + return F.ToString("wifi_ssid=[", strings.Join(r.ssidList, " "), "]") +} From 107257f5a6e222c5a60da16d760f6b54b48ea719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sun, 19 Nov 2023 12:16:35 +0800 Subject: [PATCH 08/11] Update gVisor to 20231113.0 --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 1420aeb624..076386f040 100644 --- a/go.mod +++ b/go.mod @@ -23,17 +23,17 @@ require ( github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950 - github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab + github.com/sagernet/gvisor v0.0.0-20231119034329-07cfb6aaf930 github.com/sagernet/quic-go v0.40.0 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 - github.com/sagernet/sing v0.2.18-0.20231117150934-256fafcd99b6 + github.com/sagernet/sing v0.2.18-0.20231119032432-6a556bfa50cc github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358 github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 github.com/sagernet/sing-quic v0.1.4-0.20231114135334-e2a6aab55cca github.com/sagernet/sing-shadowsocks v0.2.5 github.com/sagernet/sing-shadowsocks2 v0.1.4 github.com/sagernet/sing-shadowtls v0.1.4 - github.com/sagernet/sing-tun v0.1.20-0.20231116102736-3fa4ee409a9d + github.com/sagernet/sing-tun v0.1.20-0.20231119035513-f6ea97c5af71 github.com/sagernet/sing-vmess v0.1.8 github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 @@ -89,7 +89,7 @@ require ( golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.3.0 // indirect + golang.org/x/time v0.4.0 // indirect golang.org/x/tools v0.15.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect diff --git a/go.sum b/go.sum index 50f2cdead0..0f1bc5b62d 100644 --- a/go.sum +++ b/go.sum @@ -100,8 +100,8 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms= github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950 h1:hUz/2mJLgi7l2H36JGpDY+jou9FmI6kAm0ZkU+xPpgE= github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU= -github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab h1:u+xQoi/Yc6bNUvTfrDD6HhGRybn2lzrhf5vmS+wb4Ho= -github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab/go.mod h1:3akUhSHSVtLuJaYcW5JPepUraBOW06Ibz2HKwaK5rOk= +github.com/sagernet/gvisor v0.0.0-20231119034329-07cfb6aaf930 h1:dSPgjIw0CT6ISLeEh8Q20dZMBMFCcEceo23+LncRcNQ= +github.com/sagernet/gvisor v0.0.0-20231119034329-07cfb6aaf930/go.mod h1:JpKHkOYgh4wLwrX2BhH3ZIvCvazCkTnPeEcmigZJfHY= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/quic-go v0.40.0 h1:DvQNPb72lzvNQDe9tcUyHTw8eRv6PLtM2mNYmdlzUMo= @@ -110,8 +110,8 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= -github.com/sagernet/sing v0.2.18-0.20231117150934-256fafcd99b6 h1:v6QQn8FPEwF2Wi4jQ0xWg7+NUWMIsnP8uoAG7lua1Qo= -github.com/sagernet/sing v0.2.18-0.20231117150934-256fafcd99b6/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= +github.com/sagernet/sing v0.2.18-0.20231119032432-6a556bfa50cc h1:dmU0chO0QrBpARo8sqyOc+mvPLW+qux4ca16kb2WIc8= +github.com/sagernet/sing v0.2.18-0.20231119032432-6a556bfa50cc/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358 h1:psJQg/KXVQTupFFR1liaCAucW+NwCDhe1oYfkmz8EJ8= github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358/go.mod h1:vtUimtf7Nq9EdvD5WTpfCr69KL1M7bcgOVKiYBiAY/c= github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 h1:ncKb5tVOsCQgCsv6UpsA0jinbNb5OQ5GMPJlyQP3EHM= @@ -124,8 +124,8 @@ github.com/sagernet/sing-shadowsocks2 v0.1.4 h1:vht2M8t3m5DTgXR2j24KbYOygG5aOp+M github.com/sagernet/sing-shadowsocks2 v0.1.4/go.mod h1:Mgdee99NxxNd5Zld3ixIs18yVs4x2dI2VTDDE1N14Wc= github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= -github.com/sagernet/sing-tun v0.1.20-0.20231116102736-3fa4ee409a9d h1:FlCho4lZEMC6N6dThTVd2rOkWPW0iTqgy7YEAboeyps= -github.com/sagernet/sing-tun v0.1.20-0.20231116102736-3fa4ee409a9d/go.mod h1:B1y93eY/4pNklxrstVGFrDrH4dUZAiZFBQ4MFn6HJXA= +github.com/sagernet/sing-tun v0.1.20-0.20231119035513-f6ea97c5af71 h1:TQ3ShcLJjF3BlAefci+KjdrNC1kAg02L5ChTnDhqFRk= +github.com/sagernet/sing-tun v0.1.20-0.20231119035513-f6ea97c5af71/go.mod h1:hyzA4gDWbeg2SXklqPDswBKa//QcjlZqKw9aPcNdQ9A= github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc= github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= @@ -197,8 +197,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY= +golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= From 18dfbd1ceff1e820f9c8bd15c7b31c0141d0ce0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sun, 19 Nov 2023 12:17:06 +0800 Subject: [PATCH 09/11] Add test for ss2022 EIH --- test/go.mod | 24 ++++++------- test/go.sum | 57 ++++++++++++++--------------- test/shadowsocks_test.go | 78 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 43 deletions(-) diff --git a/test/go.mod b/test/go.mod index 40ceb54ffc..f786ea96d7 100644 --- a/test/go.mod +++ b/test/go.mod @@ -10,10 +10,10 @@ require ( github.com/docker/docker v24.0.7+incompatible github.com/docker/go-connections v0.4.0 github.com/gofrs/uuid/v5 v5.0.0 - github.com/sagernet/quic-go v0.0.0-20231008035953-32727fef9460 - github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c + github.com/sagernet/quic-go v0.40.0 + github.com/sagernet/sing v0.2.18-0.20231119032432-6a556bfa50cc github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358 - github.com/sagernet/sing-quic v0.1.4-0.20231111111624-370e6abf6769 + github.com/sagernet/sing-quic v0.1.4-0.20231114135334-e2a6aab55cca github.com/sagernet/sing-shadowsocks v0.2.5 github.com/sagernet/sing-shadowsocks2 v0.1.4 github.com/spyzhov/ajson v0.9.0 @@ -45,7 +45,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/btree v1.1.2 // indirect - github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect + github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c // indirect github.com/josharian/native v1.1.0 // indirect @@ -59,7 +59,7 @@ require ( github.com/miekg/dns v1.1.56 // indirect github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect - github.com/onsi/ginkgo/v2 v2.9.5 // indirect + github.com/onsi/ginkgo/v2 v2.9.7 // indirect github.com/ooni/go-libtor v1.1.8 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect @@ -68,16 +68,16 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-20 v0.3.4 // indirect + github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a // indirect github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a // indirect github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect - github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab // indirect + github.com/sagernet/gvisor v0.0.0-20231119034329-07cfb6aaf930 // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 // indirect github.com/sagernet/sing-shadowtls v0.1.4 // indirect - github.com/sagernet/sing-tun v0.1.20-0.20231116102736-3fa4ee409a9d // indirect + github.com/sagernet/sing-tun v0.1.20-0.20231119035513-f6ea97c5af71 // indirect github.com/sagernet/sing-vmess v0.1.8 // indirect github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 // indirect @@ -92,12 +92,12 @@ require ( go.uber.org/zap v1.26.0 // indirect go4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect golang.org/x/crypto v0.15.0 // indirect - golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect - golang.org/x/mod v0.13.0 // indirect + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + golang.org/x/mod v0.14.0 // indirect golang.org/x/sys v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.14.0 // indirect + golang.org/x/time v0.4.0 // indirect + golang.org/x/tools v0.15.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect google.golang.org/grpc v1.59.0 // indirect google.golang.org/protobuf v1.31.0 // indirect diff --git a/test/go.sum b/test/go.sum index 53a7698337..d1c08fd63b 100644 --- a/test/go.sum +++ b/test/go.sum @@ -9,9 +9,6 @@ github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/ github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/caddyserver/certmagic v0.19.2 h1:HZd1AKLx4592MalEGQS39DKs2ZOAJCEM/xYPMQ2/ui0= github.com/caddyserver/certmagic v0.19.2/go.mod h1:fsL01NomQ6N+kE2j37ZCnig2MFosG+MIO4ztnmG/zz8= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cloudflare/circl v1.3.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg= github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw= @@ -58,11 +55,10 @@ github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk= +github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c h1:PgxFEySCI41sH0mB7/2XswdXbUykQsRUGod8Rn+NubM= github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= @@ -94,9 +90,9 @@ github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3 github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= -github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= -github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= +github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= +github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= github.com/ooni/go-libtor v1.1.8 h1:Wo3V3DVTxl5vZdxtQakqYP+DAHx7pPtAFSl1bnAa08w= github.com/ooni/go-libtor v1.1.8/go.mod h1:q1YyLwRD9GeMyeerVvwc0vJ2YgwDLTp2bdVcrh/JXyI= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -113,40 +109,40 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg= -github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= +github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM= github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a h1:wZHruBxZCsQLXHAozWpnJBL3wJ/XufDpz0qKtgpSnA4= github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a/go.mod h1:dNV1ZP9y3qx5ltULeKaQZTZWTLHflgW5DES+Ses7cMI= github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA= github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms= -github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab h1:u+xQoi/Yc6bNUvTfrDD6HhGRybn2lzrhf5vmS+wb4Ho= -github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab/go.mod h1:3akUhSHSVtLuJaYcW5JPepUraBOW06Ibz2HKwaK5rOk= +github.com/sagernet/gvisor v0.0.0-20231119034329-07cfb6aaf930 h1:dSPgjIw0CT6ISLeEh8Q20dZMBMFCcEceo23+LncRcNQ= +github.com/sagernet/gvisor v0.0.0-20231119034329-07cfb6aaf930/go.mod h1:JpKHkOYgh4wLwrX2BhH3ZIvCvazCkTnPeEcmigZJfHY= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= -github.com/sagernet/quic-go v0.0.0-20231008035953-32727fef9460 h1:dAe4OIJAtE0nHOzTHhAReQteh3+sa63rvXbuIpbeOTY= -github.com/sagernet/quic-go v0.0.0-20231008035953-32727fef9460/go.mod h1:uJGpmJCOcMQqMlHKc3P1Vz6uygmpz4bPeVIoOhdVQnM= +github.com/sagernet/quic-go v0.40.0 h1:DvQNPb72lzvNQDe9tcUyHTw8eRv6PLtM2mNYmdlzUMo= +github.com/sagernet/quic-go v0.40.0/go.mod h1:VqtdhlbkeeG5Okhb3eDMb/9o0EoglReHunNT9ukrJAI= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= -github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c h1:uask61Pxc3nGqsOSjqnBKrwfODWRoEa80lXm04LNk0E= -github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= +github.com/sagernet/sing v0.2.18-0.20231119032432-6a556bfa50cc h1:dmU0chO0QrBpARo8sqyOc+mvPLW+qux4ca16kb2WIc8= +github.com/sagernet/sing v0.2.18-0.20231119032432-6a556bfa50cc/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358 h1:psJQg/KXVQTupFFR1liaCAucW+NwCDhe1oYfkmz8EJ8= github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358/go.mod h1:vtUimtf7Nq9EdvD5WTpfCr69KL1M7bcgOVKiYBiAY/c= github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 h1:ncKb5tVOsCQgCsv6UpsA0jinbNb5OQ5GMPJlyQP3EHM= github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07/go.mod h1:u/MZf32xPG8jEKe3t+xUV67EBnKtDtCaPhsJQOQGUYU= -github.com/sagernet/sing-quic v0.1.4-0.20231111111624-370e6abf6769 h1:V84NPJKirL/oDkvUh9Dor2gMZ+TTNHK3jcedqRkgcQA= -github.com/sagernet/sing-quic v0.1.4-0.20231111111624-370e6abf6769/go.mod h1:iggBfFE2ojS2yYQU8Hnrkm029RsHPdYYIKWqeCI64HE= +github.com/sagernet/sing-quic v0.1.4-0.20231114135334-e2a6aab55cca h1:wGQhe7D8Y4D3lPK8Obtv4IQAvnkEKMMu8Uv/BiYpyWc= +github.com/sagernet/sing-quic v0.1.4-0.20231114135334-e2a6aab55cca/go.mod h1:B6OgRz+qLn3N1114dcZVExkdarArtsAX2MgWJIfB72c= github.com/sagernet/sing-shadowsocks v0.2.5 h1:qxIttos4xu6ii7MTVJYA8EFQR7Q3KG6xMqmLJIFtBaY= github.com/sagernet/sing-shadowsocks v0.2.5/go.mod h1:MGWGkcU2xW2G2mfArT9/QqpVLOGU+dBaahZCtPHdt7A= github.com/sagernet/sing-shadowsocks2 v0.1.4 h1:vht2M8t3m5DTgXR2j24KbYOygG5aOp+MUhpQnAux728= github.com/sagernet/sing-shadowsocks2 v0.1.4/go.mod h1:Mgdee99NxxNd5Zld3ixIs18yVs4x2dI2VTDDE1N14Wc= github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= -github.com/sagernet/sing-tun v0.1.20-0.20231116102736-3fa4ee409a9d h1:FlCho4lZEMC6N6dThTVd2rOkWPW0iTqgy7YEAboeyps= -github.com/sagernet/sing-tun v0.1.20-0.20231116102736-3fa4ee409a9d/go.mod h1:B1y93eY/4pNklxrstVGFrDrH4dUZAiZFBQ4MFn6HJXA= +github.com/sagernet/sing-tun v0.1.20-0.20231119035513-f6ea97c5af71 h1:TQ3ShcLJjF3BlAefci+KjdrNC1kAg02L5ChTnDhqFRk= +github.com/sagernet/sing-tun v0.1.20-0.20231119035513-f6ea97c5af71/go.mod h1:hyzA4gDWbeg2SXklqPDswBKa//QcjlZqKw9aPcNdQ9A= github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc= github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= @@ -196,12 +192,12 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -213,11 +209,10 @@ golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -236,14 +231,14 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY= +golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/test/shadowsocks_test.go b/test/shadowsocks_test.go index f88715861a..7d063d9a96 100644 --- a/test/shadowsocks_test.go +++ b/test/shadowsocks_test.go @@ -74,6 +74,23 @@ func TestShadowsocks2022(t *testing.T) { } } +func TestShadowsocks2022EIH(t *testing.T) { + for _, method16 := range []string{ + "2022-blake3-aes-128-gcm", + } { + t.Run(method16, func(t *testing.T) { + testShadowsocks2022EIH(t, method16, mkBase64(t, 16)) + }) + } + for _, method32 := range []string{ + "2022-blake3-aes-256-gcm", + } { + t.Run(method32, func(t *testing.T) { + testShadowsocks2022EIH(t, method32, mkBase64(t, 32)) + }) + } +} + func testShadowsocksInboundWithShadowsocksRust(t *testing.T, method string, password string) { startDockerContainer(t, DockerOptions{ Image: ImageShadowsocksRustClient, @@ -252,6 +269,67 @@ func TestShadowsocksUoT(t *testing.T) { testSuit(t, clientPort, testPort) } +func testShadowsocks2022EIH(t *testing.T, method string, password string) { + startInstance(t, option.Options{ + Inbounds: []option.Inbound{ + { + Type: C.TypeMixed, + Tag: "mixed-in", + MixedOptions: option.HTTPMixedInboundOptions{ + ListenOptions: option.ListenOptions{ + Listen: option.NewListenAddress(netip.IPv4Unspecified()), + ListenPort: clientPort, + }, + }, + }, + { + Type: C.TypeShadowsocks, + ShadowsocksOptions: option.ShadowsocksInboundOptions{ + ListenOptions: option.ListenOptions{ + Listen: option.NewListenAddress(netip.IPv4Unspecified()), + ListenPort: serverPort, + }, + Method: method, + Password: password, + Users: []option.ShadowsocksUser{ + { + Password: password, + }, + }, + }, + }, + }, + Outbounds: []option.Outbound{ + { + Type: C.TypeDirect, + }, + { + Type: C.TypeShadowsocks, + Tag: "ss-out", + ShadowsocksOptions: option.ShadowsocksOutboundOptions{ + ServerOptions: option.ServerOptions{ + Server: "127.0.0.1", + ServerPort: serverPort, + }, + Method: method, + Password: password + ":" + password, + }, + }, + }, + Route: &option.RouteOptions{ + Rules: []option.Rule{ + { + DefaultOptions: option.DefaultRule{ + Inbound: []string{"mixed-in"}, + Outbound: "ss-out", + }, + }, + }, + }, + }) + testSuit(t, clientPort, testPort) +} + func mkBase64(t *testing.T, length int) string { psk := make([]byte, length) _, err := rand.Read(psk) From 3c5f65886381463d38062465801bb00a579c6022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 9 Nov 2023 17:04:08 +0800 Subject: [PATCH 10/11] documentation: Bump version & Refactor docs --- Makefile | 10 +- docs/changelog.md | 109 +++++++ docs/clients/android/features.md | 64 ++++ docs/clients/android/index.md | 22 ++ docs/clients/apple/features.md | 52 ++++ docs/clients/apple/index.md | 32 ++ docs/clients/general.md | 63 ++++ docs/clients/index.md | 13 + docs/clients/index.zh.md | 12 + docs/clients/privacy.md | 8 + docs/configuration/dns/rule.md | 32 +- docs/configuration/dns/rule.zh.md | 30 +- docs/configuration/dns/server.md | 4 +- docs/configuration/experimental/index.md | 8 +- docs/configuration/experimental/index.zh.md | 4 +- docs/configuration/inbound/http.md | 2 +- docs/configuration/inbound/http.zh.md | 2 +- docs/configuration/inbound/hysteria.md | 2 +- docs/configuration/inbound/hysteria2.md | 13 +- docs/configuration/inbound/hysteria2.zh.md | 10 +- docs/configuration/inbound/mixed.md | 2 +- docs/configuration/inbound/mixed.zh.md | 2 +- docs/configuration/inbound/naive.md | 2 +- docs/configuration/inbound/redirect.md | 2 +- docs/configuration/inbound/redirect.zh.md | 2 +- docs/configuration/inbound/tproxy.md | 2 +- docs/configuration/inbound/tproxy.zh.md | 2 +- docs/configuration/inbound/trojan.md | 2 +- docs/configuration/inbound/trojan.zh.md | 2 +- docs/configuration/inbound/tuic.md | 2 +- docs/configuration/inbound/tun.md | 12 +- docs/configuration/inbound/tun.zh.md | 10 +- docs/configuration/outbound/hysteria.md | 2 +- docs/configuration/outbound/hysteria2.md | 10 +- docs/configuration/outbound/hysteria2.zh.md | 6 + docs/configuration/outbound/selector.md | 2 +- docs/configuration/outbound/selector.zh.md | 2 +- docs/configuration/outbound/shadowsocksr.md | 2 +- docs/configuration/outbound/tor.md | 2 +- docs/configuration/outbound/tuic.md | 2 +- docs/configuration/outbound/wireguard.md | 4 +- docs/configuration/route/index.md | 8 +- docs/configuration/route/index.zh.md | 8 +- docs/configuration/route/rule.md | 30 +- docs/configuration/route/rule.zh.md | 30 +- docs/configuration/shared/dial.md | 2 +- docs/configuration/shared/dial.zh.md | 2 +- docs/configuration/shared/tls.md | 10 +- docs/configuration/shared/v2ray-transport.md | 4 +- docs/contributing/environment.md | 50 --- docs/contributing/index.md | 17 -- docs/contributing/sub-projects.md | 67 ---- docs/deprecated.md | 6 + docs/deprecated.zh.md | 17 ++ docs/examples/clash-api.md | 52 ---- docs/examples/dns-hijack.md | 65 ---- docs/examples/dns-hijack.zh.md | 65 ---- docs/examples/fakeip.md | 106 ------- docs/examples/fakeip.zh.md | 106 ------- docs/examples/index.md | 11 - docs/examples/index.zh.md | 11 - docs/examples/linux-server-installation.md | 38 --- docs/examples/linux-server-installation.zh.md | 38 --- docs/examples/shadowsocks.md | 163 ---------- docs/examples/shadowtls.md | 70 ----- docs/examples/tun.md | 89 ------ docs/faq/fakeip.md | 19 -- docs/faq/fakeip.zh.md | 18 -- docs/faq/index.md | 58 ---- docs/faq/index.zh.md | 50 --- docs/faq/known-issues.md | 20 -- docs/faq/known-issues.zh.md | 19 -- docs/features.md | 121 -------- docs/index.md | 2 +- docs/index.zh.md | 2 +- docs/installation/build-from-source.md | 63 ++++ docs/installation/clients/sfa.md | 23 -- docs/installation/clients/sfa.zh.md | 23 -- docs/installation/clients/sfi.md | 23 -- docs/installation/clients/sfi.zh.md | 23 -- docs/installation/clients/sfm.md | 29 -- docs/installation/clients/sfm.zh.md | 29 -- docs/installation/clients/sft.md | 30 -- docs/installation/clients/sft.zh.md | 31 -- docs/installation/clients/specification.md | 25 -- docs/installation/docker.md | 31 ++ docs/installation/from-source.md | 50 --- docs/installation/from-source.zh.md | 39 --- docs/installation/package-manager.md | 66 ++++ docs/installation/package-manager/android.md | 7 - docs/installation/package-manager/macOS.md | 14 - docs/installation/package-manager/windows.md | 13 - docs/installation/scripts/arch-install.sh | 22 ++ docs/installation/scripts/deb-install.sh | 23 ++ docs/installation/scripts/rpm-install.sh | 22 ++ docs/manual/proxy-protocol/hysteria2.md | 211 +++++++++++++ docs/manual/proxy-protocol/shadowsocks.md | 126 ++++++++ docs/manual/proxy-protocol/trojan.md | 214 +++++++++++++ docs/manual/proxy-protocol/tuic.md | 208 +++++++++++++ docs/manual/proxy/client.md | 287 ++++++++++++++++++ docs/manual/proxy/server.md | 10 + docs/manual/proxy/tun.md | 66 ++++ docs/support.md | 17 +- docs/support.zh.md | 18 +- mkdocs.yml | 145 +++++---- 105 files changed, 2061 insertions(+), 1767 deletions(-) create mode 100644 docs/clients/android/features.md create mode 100644 docs/clients/android/index.md create mode 100644 docs/clients/apple/features.md create mode 100644 docs/clients/apple/index.md create mode 100644 docs/clients/general.md create mode 100644 docs/clients/index.md create mode 100644 docs/clients/index.zh.md create mode 100644 docs/clients/privacy.md delete mode 100644 docs/contributing/environment.md delete mode 100644 docs/contributing/index.md delete mode 100644 docs/contributing/sub-projects.md create mode 100644 docs/deprecated.zh.md delete mode 100644 docs/examples/clash-api.md delete mode 100644 docs/examples/dns-hijack.md delete mode 100644 docs/examples/dns-hijack.zh.md delete mode 100644 docs/examples/fakeip.md delete mode 100644 docs/examples/fakeip.zh.md delete mode 100644 docs/examples/index.md delete mode 100644 docs/examples/index.zh.md delete mode 100644 docs/examples/linux-server-installation.md delete mode 100644 docs/examples/linux-server-installation.zh.md delete mode 100644 docs/examples/shadowsocks.md delete mode 100644 docs/examples/shadowtls.md delete mode 100644 docs/examples/tun.md delete mode 100644 docs/faq/fakeip.md delete mode 100644 docs/faq/fakeip.zh.md delete mode 100644 docs/faq/index.md delete mode 100644 docs/faq/index.zh.md delete mode 100644 docs/faq/known-issues.md delete mode 100644 docs/faq/known-issues.zh.md delete mode 100644 docs/features.md create mode 100644 docs/installation/build-from-source.md delete mode 100644 docs/installation/clients/sfa.md delete mode 100644 docs/installation/clients/sfa.zh.md delete mode 100644 docs/installation/clients/sfi.md delete mode 100644 docs/installation/clients/sfi.zh.md delete mode 100644 docs/installation/clients/sfm.md delete mode 100644 docs/installation/clients/sfm.zh.md delete mode 100644 docs/installation/clients/sft.md delete mode 100644 docs/installation/clients/sft.zh.md delete mode 100644 docs/installation/clients/specification.md create mode 100644 docs/installation/docker.md delete mode 100644 docs/installation/from-source.md delete mode 100644 docs/installation/from-source.zh.md create mode 100644 docs/installation/package-manager.md delete mode 100644 docs/installation/package-manager/android.md delete mode 100644 docs/installation/package-manager/macOS.md delete mode 100644 docs/installation/package-manager/windows.md create mode 100644 docs/installation/scripts/arch-install.sh create mode 100644 docs/installation/scripts/deb-install.sh create mode 100644 docs/installation/scripts/rpm-install.sh create mode 100644 docs/manual/proxy-protocol/hysteria2.md create mode 100644 docs/manual/proxy-protocol/shadowsocks.md create mode 100644 docs/manual/proxy-protocol/trojan.md create mode 100644 docs/manual/proxy-protocol/tuic.md create mode 100644 docs/manual/proxy/client.md create mode 100644 docs/manual/proxy/server.md create mode 100644 docs/manual/proxy/tun.md diff --git a/Makefile b/Makefile index ed62162a28..f1889ffc50 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ MAIN_PARAMS = $(PARAMS) -tags $(TAGS) MAIN = ./cmd/sing-box PREFIX ?= $(shell go env GOPATH) -.PHONY: test release +.PHONY: test release docs build: go build $(MAIN_PARAMS) $(MAIN) @@ -182,6 +182,14 @@ lib_install: go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.0.0-20230915142329-c6740b6d2950 go install -v github.com/sagernet/gomobile/cmd/gobind@v0.0.0-20230915142329-c6740b6d2950 +docs: + mkdocs serve + +publish_docs: + mkdocs gh-deploy -m "Update" --force --ignore-version --no-history + +docs_install: + pip install --force-reinstall mkdocs-material=="9.*" mkdocs-static-i18n=="1.2.*" clean: rm -rf bin dist sing-box rm -f $(shell go env GOPATH)/sing-box diff --git a/docs/changelog.md b/docs/changelog.md index cb2316f9f2..a1edd1a959 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,17 +1,63 @@ +--- +icon: material/alert-decagram +--- + +# ChangeLog + +#### 1.7.0-beta.5 + +* Update gVisor to 20231113.0 +* Fixes and improvements + +#### 1.7.0-beta.4 + +* Add `wifi_ssid` and `wifi_bssid` route and DNS rules **1** +* Fixes and improvements + +**1**: + +Only supported in graphical clients on Android and iOS. + +#### 1.7.0-beta.3 + +* Fix zero TTL was incorrectly reset +* Fixes and improvements + #### 1.6.5 * Fix crash if TUIC inbound authentication failed * Fixes and improvements +#### 1.7.0-beta.2 + +* Fix crash if TUIC inbound authentication failed +* Update quic-go to v0.40.0 +* Fixes and improvements + #### 1.6.4 * Fixes and improvements +#### 1.7.0-beta.1 + +* Fixes and improvements + #### 1.6.3 * iOS/Android: Fix profile auto update * Fixes and improvements +#### 1.7.0-alpha.11 + +* iOS/Android: Fix profile auto update +* Fixes and improvements + +#### 1.7.0-alpha.10 + +* Fix tcp-brutal not working with TLS +* Fix Android client not closing in some cases +* Fixes and improvements + #### 1.6.2 * Fixes and improvements @@ -21,6 +67,34 @@ * Our [Android client](/installation/clients/sfa) is now available in the Google Play Store ▶️ * Fixes and improvements +#### 1.7.0-alpha.6 + +* Fixes and improvements + +#### 1.7.0-alpha.4 + +* Migrate multiplex and UoT server to inbound **1** +* Add TCP Brutal support for multiplex **2** + +**1**: + +Starting in 1.7.0, multiplexing support is no longer enabled by default and needs to be turned on explicitly in inbound options. + +**2** + +Hysteria Brutal Congestion Control Algorithm in TCP. A kernel module needs to be installed on the Linux server, see [TCP Brutal](/configuration/shared/tcp-brutal) for details. + +#### 1.7.0-alpha.3 + +* Add [HTTPUpgrade V2Ray transport](/configuration/shared/v2ray-transport#HTTPUpgrade) support **1** +* Fixes and improvements + +**1**: + +Introduced in V2Ray 5.10.0. + +The new HTTPUpgrade transport has better performance than WebSocket and is better suited for CDN abuse. + #### 1.6.0 * Fixes and improvements @@ -45,6 +119,23 @@ This update is intended to address the multi-send defects of the old implementat Based on discussions with the original author, the brutal CC and QUIC protocol parameters of the old protocol (Hysteria 1) have been updated to be consistent with Hysteria 2 +#### 1.7.0-alpha.2 + +* Fix bugs introduced in 1.7.0-alpha.1 + +#### 1.7.0-alpha.1 + +* Add [exclude route support](/configuration/inbound/tun) for TUN inbound +* Add `udp_disable_domain_unmapping` [inbound listen option](/configuration/shared/listen) **1** +* Fixes and improvements + +**1**: + +If enabled, for UDP proxy requests addressed to a domain, +the original packet address will be sent in the response instead of the mapped domain. + +This option is used for compatibility with clients that +do not support receiving UDP packets with domain addresses, such as Surge. #### 1.5.5 @@ -106,6 +197,24 @@ the old protocol (Hysteria 1) have been updated to be consistent with Hysteria 2 * Update golang.org/x/net to v0.17.0 * Fixes and improvements +#### 1.6.0-beta.3 + +* Update the legacy Hysteria protocol **1** +* Fixes and improvements + +**1** + +Based on discussions with the original author, the brutal CC and QUIC protocol parameters of +the old protocol (Hysteria 1) have been updated to be consistent with Hysteria 2 + +#### 1.6.0-beta.2 + +* Add TLS self sign key pair generate command +* Update brutal congestion control for Hysteria2 +* Fix Clash cache crash on arm32 devices +* Update golang.org/x/net to v0.17.0 +* Fixes and improvements + #### 1.5.3 * Fix compatibility with Android 14 diff --git a/docs/clients/android/features.md b/docs/clients/android/features.md new file mode 100644 index 0000000000..2702e6fa8c --- /dev/null +++ b/docs/clients/android/features.md @@ -0,0 +1,64 @@ +# :material-decagram: Features + +#### UI options + +* Display realtime network speed in the notification + +#### Service + +SFA allows you to run sing-box through ForegroundService or VpnService (when TUN is required). + +#### TUN + +SFA provides an unprivileged TUN implementation through Android VpnService. + +| TUN inbound option | Available | Note | +|-------------------------------|------------------|--------------------| +| `interface_name` | :material-close: | Managed by Android | +| `inet4_address` | :material-check: | / | +| `inet6_address` | :material-check: | / | +| `mtu` | :material-check: | / | +| `auto_route` | :material-check: | / | +| `strict_route` | :material-close: | Not implemented | +| `inet4_route_address` | :material-check: | / | +| `inet6_route_address` | :material-check: | / | +| `inet4_route_exclude_address` | :material-check: | / | +| `inet6_route_exclude_address` | :material-check: | / | +| `endpoint_independent_nat` | :material-check: | / | +| `stack` | :material-check: | / | +| `include_interface` | :material-close: | No permission | +| `exclude_interface` | :material-close: | No permission | +| `include_uid` | :material-close: | No permission | +| `exclude_uid` | :material-close: | No permission | +| `include_android_user` | :material-close: | No permission | +| `include_package` | :material-check: | / | +| `exclude_package` | :material-check: | / | +| `platform` | :material-check: | / | + +| Route/DNS rule option | Available | Note | +|-----------------------|------------------|-----------------------------------| +| `process_name` | :material-close: | No permission | +| `process_path` | :material-close: | No permission | +| `package_name` | :material-check: | / | +| `user` | :material-close: | Use `package_name` instead | +| `user_id` | :material-close: | Use `package_name` instead | +| `wifi_ssid` | :material-check: | Fine location permission required | +| `wifi_bssid` | :material-check: | Fine location permission required | + +### Override + +Overrides profile configuration items with platform-specific values. + +#### Per-app proxy + +SFA allows you to select a list of Android apps that require proxying or bypassing in the graphical interface to +override the `include_package` and `exclude_package` configuration items. + +In particular, the selector also provides the “China apps” scanning feature, providing Chinese users with an excellent +experience to bypass apps that do not require a proxy. Specifically, by scanning China application or SDK +characteristics through dex class path and other means, there will be almost no missed reports. + +### Chore + +* The working directory is located at `/sdcard/Android/data/io.nekohasekai.sfa/files` (External files directory) +* Crash logs is located in `$working_directory/stderr.log` diff --git a/docs/clients/android/index.md b/docs/clients/android/index.md new file mode 100644 index 0000000000..babef2831f --- /dev/null +++ b/docs/clients/android/index.md @@ -0,0 +1,22 @@ +--- +icon: material/android +--- + +# sing-box for Android + +SFA allows users to manage and run local or remote sing-box configuration files, and provides +platform-specific function implementation, such as TUN transparent proxy implementation. + +## :material-graph: Requirements + +* Android 5.0+ + +## :material-download: Download + +* [Play Store](https://play.google.com/store/apps/details?id=io.nekohasekai.sfa) +* [Play Store (Beta)](https://play.google.com/apps/testing/io.nekohasekai.sfa) +* [Github Releases](https://github.com/SagerNet/sing-box/releases) + +## :material-source-repository: Source code + +* [Github](https://github.com/SagerNet/sing-box-for-android) diff --git a/docs/clients/apple/features.md b/docs/clients/apple/features.md new file mode 100644 index 0000000000..95143d98f8 --- /dev/null +++ b/docs/clients/apple/features.md @@ -0,0 +1,52 @@ +# :material-decagram: Features + +#### UI options + +* Always On +* Include All Networks (Proxy traffic for LAN and cellular services) +* (Apple tvOS) Import profile from iPhone/iPad + +#### Service + +SFI/SFM/SFT allows you to run sing-box through NetworkExtension with Application Extension or System Extension. + +#### TUN + +SFI/SFM/SFT provides an unprivileged TUN implementation through NetworkExtension. + +| TUN inbound option | Available | Note | +|-------------------------------|-----------|-------------------| +| `interface_name` | ✖️ | Managed by Darwin | +| `inet4_address` | ✔️ | / | +| `inet6_address` | ✔️ | / | +| `mtu` | ✔️ | / | +| `auto_route` | ✔️ | / | +| `strict_route` | ✖️ | Not implemented | +| `inet4_route_address` | ✔️ | / | +| `inet6_route_address` | ✔️ | / | +| `inet4_route_exclude_address` | ✔️ | / | +| `inet6_route_exclude_address` | ✔️ | / | +| `endpoint_independent_nat` | ✔️ | / | +| `stack` | ✔️ | / | +| `include_interface` | ✖️ | Not implemented | +| `exclude_interface` | ✖️ | Not implemented | +| `include_uid` | ✖️ | Not implemented | +| `exclude_uid` | ✖️ | Not implemented | +| `include_android_user` | ✖️ | Not implemented | +| `include_package` | ✖️ | Not implemented | +| `exclude_package` | ✖️ | Not implemented | +| `platform` | ✔️ | / | + +| Route/DNS rule option | Available | Note | +|-----------------------|------------------|-----------------------| +| `process_name` | :material-close: | No permission | +| `process_path` | :material-close: | No permission | +| `package_name` | :material-close: | / | +| `user` | :material-close: | No permission | +| `user_id` | :material-close: | No permission | +| `wifi_ssid` | :material-alert: | Only supported on iOS | +| `wifi_bssid` | :material-alert: | Only supported on iOS | + +### Chore + +* Crash logs is located in `Settings` -> `View Service Log` diff --git a/docs/clients/apple/index.md b/docs/clients/apple/index.md new file mode 100644 index 0000000000..72cf5f4624 --- /dev/null +++ b/docs/clients/apple/index.md @@ -0,0 +1,32 @@ +--- +icon: material/apple +--- + +# sing-box for Apple platforms + +SFI/SFM/SFT allows users to manage and run local or remote sing-box configuration files, and provides +platform-specific function implementation, such as TUN transparent proxy implementation. + +## :material-graph: Requirements + +* iOS 15.0+ / macOS 13.0+ / Apple tvOS 17.0+ +* An Apple account outside of mainland China + +## :material-download: Download + +* [AppStore](https://apps.apple.com/us/app/sing-box/id6451272673) +* [TestFlight (Beta)](https://testflight.apple.com/join/AcqO44FH) + +## :material-file-download: Download (macOS standalone version) + +* [Homebrew Cask](https://formulae.brew.sh/cask/sfm) + +```bash +brew install sfm +``` + +* [Github Releases](https://github.com/SagerNet/sing-box/releases) + +## :material-source-repository: Source code + +* [Github](https://github.com/SagerNet/sing-box-for-apple) diff --git a/docs/clients/general.md b/docs/clients/general.md new file mode 100644 index 0000000000..4d57c991ce --- /dev/null +++ b/docs/clients/general.md @@ -0,0 +1,63 @@ +--- +icon: material/pencil-ruler +--- + +# General + +Describes and explains the functions implemented uniformly by sing-box graphical clients. + +### Profile + +Profile describes a sing-box configuration file and its state. + +#### Local + +* Local Profile represents a local sing-box configuration with minimal state +* The graphical client must provide an editor to modify configuration content + +#### iCloud (on iOS and macOS) + +* iCloud Profile represents a remote sing-box configuration with iCloud as the update source +* The configuration file is stored in the sing-box folder under iCloud +* The graphical client must provide an editor to modify configuration content + +#### Remote + +* Remote Profile represents a remote sing-box configuration with a URL as the update source. +* The graphical client should provide a configuration content viewer +* The graphical client must implement automatic profile update (default interval is 60 minutes) and HTTP Basic + authorization. + +At the same time, the graphical client must provide support for importing remote profiles +through a specific URL Scheme. The URL is defined as follows: + +``` +sing-box://import-remote-profile?url=urlEncodedURL#urlEncodedName +``` + +### Dashboard + +While the sing-box service is running, the graphical client should provide a Dashboard interface to manage the service. + +#### Status + +Dashboard should display status information such as memory, connection, and traffic. + +#### Mode + +Dashboard should provide a Mode selector for switching when the configuration uses at least two `clash_mode` values. + +#### Groups + +When the configuration includes group outbounds (specifically, Selector or URLTest), +the dashboard should provide a Group selector for status display or switching. + +### Chore + +#### Core + +Graphical clients should provide a Core region: + +* Display the current sing-box version +* Provides a button to clean the working directory +* Provides a memory limiter switch \ No newline at end of file diff --git a/docs/clients/index.md b/docs/clients/index.md new file mode 100644 index 0000000000..de92e9950e --- /dev/null +++ b/docs/clients/index.md @@ -0,0 +1,13 @@ +# :material-cellphone-link: Graphical Clients + +Maintained by Project S to provide a unified experience and platform-specific functionality. + +| Platform | Client | +|---------------------------------------|-----------------------------------------| +| :material-android: Android | [sing-box for Android](./android) | +| :material-apple: iOS/macOS/Apple tvOS | [sing-box for Apple platforms](./apple) | +| TODO | / | + +Some third-party projects that claim to use sing-box or use sing-box as a selling point are not listed here. The core +motivation of the maintainers of such projects is to acquire more users, and even though they provide friendly VPN +client features, the code is usually of poor quality and contains ads. diff --git a/docs/clients/index.zh.md b/docs/clients/index.zh.md new file mode 100644 index 0000000000..4446ca5f3a --- /dev/null +++ b/docs/clients/index.zh.md @@ -0,0 +1,12 @@ +# :material-cellphone-link: 图形界面客户端 + +由 Project S 维护,提供统一的体验与平台特定的功能。 + +| 平台 | 客户端 | +|---------------------------------------|-----------------------------------------| +| :material-android: Android | [sing-box for Android](./android) | +| :material-apple: iOS/macOS/Apple tvOS | [sing-box for Apple platforms](./apple) | +| TODO | / | + +此处没有列出一些声称使用或以 sing-box 为卖点的第三方项目。此类项目维护者的动机是获得更多用户,即使它们提供友好的商业 +VPN 客户端功能, 但代码质量很差且包含广告。 diff --git a/docs/clients/privacy.md b/docs/clients/privacy.md new file mode 100644 index 0000000000..f8b51afd1a --- /dev/null +++ b/docs/clients/privacy.md @@ -0,0 +1,8 @@ +--- +icon: material/security +--- + +# Privacy policy + +sing-box and official graphics clients do not collect or share personal data, +and the data generated by the software is always on your device. diff --git a/docs/configuration/dns/rule.md b/docs/configuration/dns/rule.md index f7c46db396..18e352b407 100644 --- a/docs/configuration/dns/rule.md +++ b/docs/configuration/dns/rule.md @@ -79,6 +79,12 @@ 1000 ], "clash_mode": "direct", + "wifi_ssid": [ + "My WIFI" + ], + "wifi_bssid": [ + "00:00:00:00:00:00" + ], "invert": false, "outbound": [ "direct" @@ -188,7 +194,7 @@ Match port range. #### process_name -!!! error "" +!!! quote "" Only supported on Linux, Windows, and macOS. @@ -196,7 +202,7 @@ Match process name. #### process_path -!!! error "" +!!! quote "" Only supported on Linux, Windows, and macOS. @@ -208,7 +214,7 @@ Match android package name. #### user -!!! error "" +!!! quote "" Only supported on Linux. @@ -216,7 +222,7 @@ Match user name. #### user_id -!!! error "" +!!! quote "" Only supported on Linux. @@ -226,6 +232,24 @@ Match user id. Match Clash mode. +#### wifi_ssid + + + +!!! quote "" + + Only supported in graphical clients on Android and iOS. + +Match WiFi SSID. + +#### wifi_bssid + +!!! quote "" + + Only supported in graphical clients on Android and iOS. + +Match WiFi BSSID. + #### invert Invert match result. diff --git a/docs/configuration/dns/rule.zh.md b/docs/configuration/dns/rule.zh.md index 9af71a40b3..98bfa8ab9a 100644 --- a/docs/configuration/dns/rule.zh.md +++ b/docs/configuration/dns/rule.zh.md @@ -78,6 +78,12 @@ 1000 ], "clash_mode": "direct", + "wifi_ssid": [ + "My WIFI" + ], + "wifi_bssid": [ + "00:00:00:00:00:00" + ], "invert": false, "outbound": [ "direct" @@ -185,7 +191,7 @@ DNS 查询类型。值可以为整数或者类型名称字符串。 #### process_name -!!! error "" +!!! quote "" 仅支持 Linux、Windows 和 macOS. @@ -193,7 +199,7 @@ DNS 查询类型。值可以为整数或者类型名称字符串。 #### process_path -!!! error "" +!!! quote "" 仅支持 Linux、Windows 和 macOS. @@ -205,7 +211,7 @@ DNS 查询类型。值可以为整数或者类型名称字符串。 #### user -!!! error "" +!!! quote "" 仅支持 Linux。 @@ -213,7 +219,7 @@ DNS 查询类型。值可以为整数或者类型名称字符串。 #### user_id -!!! error "" +!!! quote "" 仅支持 Linux。 @@ -223,6 +229,22 @@ DNS 查询类型。值可以为整数或者类型名称字符串。 匹配 Clash 模式。 +#### wifi_ssid + +!!! quote "" + + 仅在 Android 与 iOS 的图形客户端中支持。 + +匹配 WiFi SSID。 + +#### wifi_bssid + +!!! quote "" + + 仅在 Android 与 iOS 的图形客户端中支持。 + +匹配 WiFi BSSID。 + #### invert 反选匹配结果。 diff --git a/docs/configuration/dns/server.md b/docs/configuration/dns/server.md index 48fb1bb0c0..2ce50b38e1 100644 --- a/docs/configuration/dns/server.md +++ b/docs/configuration/dns/server.md @@ -49,7 +49,7 @@ The address of the dns server. !!! warning "" - QUIC and HTTP3 transport is not included by default, see [Installation](/#installation). + QUIC and HTTP3 transport is not included by default, see [Installation](./#installation). !!! info "" @@ -57,7 +57,7 @@ The address of the dns server. !!! warning "" - DHCP transport is not included by default, see [Installation](/#installation). + DHCP transport is not included by default, see [Installation](./#installation). | RCode | Description | |-------------------|-----------------------| diff --git a/docs/configuration/experimental/index.md b/docs/configuration/experimental/index.md index 9af9ec09a9..308e851c3b 100644 --- a/docs/configuration/experimental/index.md +++ b/docs/configuration/experimental/index.md @@ -44,9 +44,9 @@ ### Clash API Fields -!!! error "" +!!! quote "" - Clash API is not included by default, see [Installation](/#installation). + Clash API is not included by default, see [Installation](./#installation). #### external_controller @@ -110,9 +110,9 @@ If not empty, `store_selected` will use a separate store keyed by it. ### V2Ray API Fields -!!! error "" +!!! quote "" - V2Ray API is not included by default, see [Installation](/#installation). + V2Ray API is not included by default, see [Installation](./#installation). #### listen diff --git a/docs/configuration/experimental/index.zh.md b/docs/configuration/experimental/index.zh.md index 7bc2a62c9d..88a95852c9 100644 --- a/docs/configuration/experimental/index.zh.md +++ b/docs/configuration/experimental/index.zh.md @@ -44,7 +44,7 @@ ### Clash API 字段 -!!! error "" +!!! quote "" 默认安装不包含 Clash API,参阅 [安装](/zh/#_2)。 @@ -108,7 +108,7 @@ Clash 中的默认模式,默认使用 `Rule`。 ### V2Ray API 字段 -!!! error "" +!!! quote "" 默认安装不包含 V2Ray API,参阅 [安装](/zh/#_2)。 diff --git a/docs/configuration/inbound/http.md b/docs/configuration/inbound/http.md index dc80cf0aa9..3b14905015 100644 --- a/docs/configuration/inbound/http.md +++ b/docs/configuration/inbound/http.md @@ -36,7 +36,7 @@ No authentication required if empty. #### set_system_proxy -!!! error "" +!!! quote "" Only supported on Linux, Android, Windows, and macOS. diff --git a/docs/configuration/inbound/http.zh.md b/docs/configuration/inbound/http.zh.md index b7228eb9c3..2f3d44f556 100644 --- a/docs/configuration/inbound/http.zh.md +++ b/docs/configuration/inbound/http.zh.md @@ -36,7 +36,7 @@ HTTP 用户 #### set_system_proxy -!!! error "" +!!! quote "" 仅支持 Linux、Android、Windows 和 macOS。 diff --git a/docs/configuration/inbound/hysteria.md b/docs/configuration/inbound/hysteria.md index 5026010493..f027a05650 100644 --- a/docs/configuration/inbound/hysteria.md +++ b/docs/configuration/inbound/hysteria.md @@ -31,7 +31,7 @@ !!! warning "" - QUIC, which is required by hysteria is not included by default, see [Installation](/#installation). + QUIC, which is required by hysteria is not included by default, see [Installation](./#installation). ### Listen Fields diff --git a/docs/configuration/inbound/hysteria2.md b/docs/configuration/inbound/hysteria2.md index ab6f2e4dd1..4427b6517e 100644 --- a/docs/configuration/inbound/hysteria2.md +++ b/docs/configuration/inbound/hysteria2.md @@ -4,8 +4,8 @@ { "type": "hysteria2", "tag": "hy2-in", - - ... // Listen Fields + ... + // Listen Fields "up_mbps": 100, "down_mbps": 100, @@ -28,7 +28,14 @@ !!! warning "" - QUIC, which is required by Hysteria2 is not included by default, see [Installation](/#installation). + QUIC, which is required by Hysteria2 is not included by default, see [Installation](./#installation). + +!!! warning "Difference from official Hysteria2" + + The official program supports an authentication method called **userpass**, + which essentially uses a combination of `:` as the actual password, + while sing-box does not provide this alias. + To use sing-box with the official program, you need to fill in that combination as the actual password. ### Listen Fields diff --git a/docs/configuration/inbound/hysteria2.zh.md b/docs/configuration/inbound/hysteria2.zh.md index f43188c09f..4d5a94157c 100644 --- a/docs/configuration/inbound/hysteria2.zh.md +++ b/docs/configuration/inbound/hysteria2.zh.md @@ -4,8 +4,8 @@ { "type": "hysteria2", "tag": "hy2-in", - - ... // 监听字段 + ... + // 监听字段 "up_mbps": 100, "down_mbps": 100, @@ -30,6 +30,12 @@ 默认安装不包含被 Hysteria2 依赖的 QUIC,参阅 [安装](/zh/#_2)。 +!!! warning "与官方 Hysteria2 的区别" + + 官方程序支持一种名为 **userpass** 的验证方式, + 本质上上是将用户名与密码的组合 `:` 作为实际上的密码,而 sing-box 不提供此别名。 + 要将 sing-box 与官方程序一起使用, 您需要填写该组合作为实际密码。 + ### 监听字段 参阅 [监听字段](/zh/configuration/shared/listen/)。 diff --git a/docs/configuration/inbound/mixed.md b/docs/configuration/inbound/mixed.md index 9e262c7d57..62313f489d 100644 --- a/docs/configuration/inbound/mixed.md +++ b/docs/configuration/inbound/mixed.md @@ -33,7 +33,7 @@ No authentication required if empty. #### set_system_proxy -!!! error "" +!!! quote "" Only supported on Linux, Android, Windows, and macOS. diff --git a/docs/configuration/inbound/mixed.zh.md b/docs/configuration/inbound/mixed.zh.md index 8f00af14d4..448c66b464 100644 --- a/docs/configuration/inbound/mixed.zh.md +++ b/docs/configuration/inbound/mixed.zh.md @@ -33,7 +33,7 @@ SOCKS 和 HTTP 用户 #### set_system_proxy -!!! error "" +!!! quote "" 仅支持 Linux、Android、Windows 和 macOS。 diff --git a/docs/configuration/inbound/naive.md b/docs/configuration/inbound/naive.md index 35ad109616..562a70703d 100644 --- a/docs/configuration/inbound/naive.md +++ b/docs/configuration/inbound/naive.md @@ -20,7 +20,7 @@ !!! warning "" - HTTP3 transport is not included by default, see [Installation](/#installation). + HTTP3 transport is not included by default, see [Installation](./#installation). ### Listen Fields diff --git a/docs/configuration/inbound/redirect.md b/docs/configuration/inbound/redirect.md index 9ea418eef6..97736e286b 100644 --- a/docs/configuration/inbound/redirect.md +++ b/docs/configuration/inbound/redirect.md @@ -1,4 +1,4 @@ -!!! error "" +!!! quote "" Only supported on Linux and macOS. diff --git a/docs/configuration/inbound/redirect.zh.md b/docs/configuration/inbound/redirect.zh.md index 125dc73f84..a03049e5cd 100644 --- a/docs/configuration/inbound/redirect.zh.md +++ b/docs/configuration/inbound/redirect.zh.md @@ -1,4 +1,4 @@ -!!! error "" +!!! quote "" 仅支持 Linux 和 macOS。 diff --git a/docs/configuration/inbound/tproxy.md b/docs/configuration/inbound/tproxy.md index c637707294..4975931e89 100644 --- a/docs/configuration/inbound/tproxy.md +++ b/docs/configuration/inbound/tproxy.md @@ -1,4 +1,4 @@ -!!! error "" +!!! quote "" Only supported on Linux. diff --git a/docs/configuration/inbound/tproxy.zh.md b/docs/configuration/inbound/tproxy.zh.md index 9ab98e5782..6e35ad5e0c 100644 --- a/docs/configuration/inbound/tproxy.zh.md +++ b/docs/configuration/inbound/tproxy.zh.md @@ -1,4 +1,4 @@ -!!! error "" +!!! quote "" 仅支持 Linux。 diff --git a/docs/configuration/inbound/trojan.md b/docs/configuration/inbound/trojan.md index 787d2b11bf..cd45539ddc 100644 --- a/docs/configuration/inbound/trojan.md +++ b/docs/configuration/inbound/trojan.md @@ -47,7 +47,7 @@ TLS configuration, see [TLS](/configuration/shared/tls/#inbound). #### fallback -!!! error "" +!!! quote "" There is no evidence that GFW detects and blocks Trojan servers based on HTTP responses, and opening the standard http/s port on the server is a much bigger signature. diff --git a/docs/configuration/inbound/trojan.zh.md b/docs/configuration/inbound/trojan.zh.md index f52422af82..54144ae680 100644 --- a/docs/configuration/inbound/trojan.zh.md +++ b/docs/configuration/inbound/trojan.zh.md @@ -49,7 +49,7 @@ TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。 #### fallback -!!! error "" +!!! quote "" 没有证据表明 GFW 基于 HTTP 响应检测并阻止 Trojan 服务器,并且在服务器上打开标准 http/s 端口是一个更大的特征。 diff --git a/docs/configuration/inbound/tuic.md b/docs/configuration/inbound/tuic.md index 51724e5d84..d4d9aafdb2 100644 --- a/docs/configuration/inbound/tuic.md +++ b/docs/configuration/inbound/tuic.md @@ -24,7 +24,7 @@ !!! warning "" - QUIC, which is required by TUIC is not included by default, see [Installation](/#installation). + QUIC, which is required by TUIC is not included by default, see [Installation](./#installation). ### Listen Fields diff --git a/docs/configuration/inbound/tun.md b/docs/configuration/inbound/tun.md index e6c52c54c1..1d493d58e8 100644 --- a/docs/configuration/inbound/tun.md +++ b/docs/configuration/inbound/tun.md @@ -1,4 +1,4 @@ -!!! error "" +!!! quote "" Only supported on Linux, Windows and macOS. @@ -102,7 +102,7 @@ The maximum transmission unit. Set the default route to the Tun. -!!! error "" +!!! quote "" To avoid traffic loopback, set `route.auto_detect_interface` or `route.default_interface` or `outbound.bind_interface` @@ -171,11 +171,11 @@ TCP/IP stack. !!! warning "" - gVisor and LWIP stacks is not included by default, see [Installation](/#installation). + gVisor and LWIP stacks is not included by default, see [Installation](./#installation). #### include_interface -!!! error "" +!!! quote "" Interface rules are only supported on Linux and require auto_route. @@ -191,7 +191,7 @@ Conflict with `include_interface`. #### include_uid -!!! error "" +!!! quote "" UID rules are only supported on Linux and require auto_route. @@ -211,7 +211,7 @@ Exclude users in route, but in range. #### include_android_user -!!! error "" +!!! quote "" Android user and package rules are only supported on Android and require auto_route. diff --git a/docs/configuration/inbound/tun.zh.md b/docs/configuration/inbound/tun.zh.md index 8f246c042b..7ea3a6a030 100644 --- a/docs/configuration/inbound/tun.zh.md +++ b/docs/configuration/inbound/tun.zh.md @@ -1,4 +1,4 @@ -!!! error "" +!!! quote "" 仅支持 Linux、Windows 和 macOS。 @@ -102,7 +102,7 @@ tun 接口的 IPv6 前缀。 设置到 Tun 的默认路由。 -!!! error "" +!!! quote "" 为避免流量环回,请设置 `route.auto_detect_interface` 或 `route.default_interface` 或 `outbound.bind_interface`。 @@ -171,7 +171,7 @@ TCP/IP 栈。 #### include_interface -!!! error "" +!!! quote "" 接口规则仅在 Linux 下被支持,并且需要 `auto_route`。 @@ -187,7 +187,7 @@ TCP/IP 栈。 #### include_uid -!!! error "" +!!! quote "" UID 规则仅在 Linux 下被支持,并且需要 `auto_route`。 @@ -207,7 +207,7 @@ TCP/IP 栈。 #### include_android_user -!!! error "" +!!! quote "" Android 用户和应用规则仅在 Android 下被支持,并且需要 `auto_route`。 diff --git a/docs/configuration/outbound/hysteria.md b/docs/configuration/outbound/hysteria.md index 8eafa99c27..ff9974de61 100644 --- a/docs/configuration/outbound/hysteria.md +++ b/docs/configuration/outbound/hysteria.md @@ -26,7 +26,7 @@ !!! warning "" - QUIC, which is required by hysteria is not included by default, see [Installation](/#installation). + QUIC, which is required by hysteria is not included by default, see [Installation](./#installation). ### Fields diff --git a/docs/configuration/outbound/hysteria2.md b/docs/configuration/outbound/hysteria2.md index 9861e332ac..90860c1c4f 100644 --- a/docs/configuration/outbound/hysteria2.md +++ b/docs/configuration/outbound/hysteria2.md @@ -24,7 +24,15 @@ !!! warning "" - QUIC, which is required by Hysteria2 is not included by default, see [Installation](/#installation). + QUIC, which is required by Hysteria2 is not included by default, see [Installation](./#installation). + +!!! warning "Difference from official Hysteria2" + + The official Hysteria2 supports an authentication method called **userpass**, + which essentially uses a combination of `:` as the actual password, + while sing-box does not provide this alias. + If you are planning to use sing-box with the official program, + please note that you will need to fill the combination as the password. ### Fields diff --git a/docs/configuration/outbound/hysteria2.zh.md b/docs/configuration/outbound/hysteria2.zh.md index 1e490a63af..5d20802794 100644 --- a/docs/configuration/outbound/hysteria2.zh.md +++ b/docs/configuration/outbound/hysteria2.zh.md @@ -26,6 +26,12 @@ 默认安装不包含被 Hysteria2 依赖的 QUIC,参阅 [安装](/zh/#_2)。 +!!! warning "与官方 Hysteria2 的区别" + + 官方程序支持一种名为 **userpass** 的验证方式, + 本质上上是将用户名与密码的组合 `:` 作为实际上的密码,而 sing-box 不提供此别名。 + 要将 sing-box 与官方程序一起使用, 您需要填写该组合作为实际密码。 + ### 字段 #### server diff --git a/docs/configuration/outbound/selector.md b/docs/configuration/outbound/selector.md index 1d2c74a9ea..ee75358f5b 100644 --- a/docs/configuration/outbound/selector.md +++ b/docs/configuration/outbound/selector.md @@ -15,7 +15,7 @@ } ``` -!!! error "" +!!! quote "" The selector can only be controlled through the [Clash API](/configuration/experimental#clash-api-fields) currently. diff --git a/docs/configuration/outbound/selector.zh.md b/docs/configuration/outbound/selector.zh.md index 9e985ab1fa..ffe2d70ae1 100644 --- a/docs/configuration/outbound/selector.zh.md +++ b/docs/configuration/outbound/selector.zh.md @@ -15,7 +15,7 @@ } ``` -!!! error "" +!!! quote "" 选择器目前只能通过 [Clash API](/zh/configuration/experimental#clash-api) 来控制。 diff --git a/docs/configuration/outbound/shadowsocksr.md b/docs/configuration/outbound/shadowsocksr.md index 43c71b990f..0c7f1b32b7 100644 --- a/docs/configuration/outbound/shadowsocksr.md +++ b/docs/configuration/outbound/shadowsocksr.md @@ -25,7 +25,7 @@ !!! warning "" - ShadowsocksR is not included by default, see [Installation](/#installation). + ShadowsocksR is not included by default, see [Installation](./#installation). ### Fields diff --git a/docs/configuration/outbound/tor.md b/docs/configuration/outbound/tor.md index 2b0cc9f00e..fe7e4ff6f9 100644 --- a/docs/configuration/outbound/tor.md +++ b/docs/configuration/outbound/tor.md @@ -18,7 +18,7 @@ !!! info "" - Embedded tor is not included by default, see [Installation](/#installation). + Embedded tor is not included by default, see [Installation](./#installation). ### Fields diff --git a/docs/configuration/outbound/tuic.md b/docs/configuration/outbound/tuic.md index bbcc199ff2..522e78924a 100644 --- a/docs/configuration/outbound/tuic.md +++ b/docs/configuration/outbound/tuic.md @@ -23,7 +23,7 @@ !!! warning "" - QUIC, which is required by TUIC is not included by default, see [Installation](/#installation). + QUIC, which is required by TUIC is not included by default, see [Installation](./#installation). ### Fields diff --git a/docs/configuration/outbound/wireguard.md b/docs/configuration/outbound/wireguard.md index f4a8810895..c9c49c79b8 100644 --- a/docs/configuration/outbound/wireguard.md +++ b/docs/configuration/outbound/wireguard.md @@ -38,11 +38,11 @@ !!! warning "" - WireGuard is not included by default, see [Installation](/#installation). + WireGuard is not included by default, see [Installation](./#installation). !!! warning "" - gVisor, which is required by the unprivileged WireGuard is not included by default, see [Installation](/#installation). + gVisor, which is required by the unprivileged WireGuard is not included by default, see [Installation](./#installation). ### Fields diff --git a/docs/configuration/route/index.md b/docs/configuration/route/index.md index 8c6ca6e110..846d497330 100644 --- a/docs/configuration/route/index.md +++ b/docs/configuration/route/index.md @@ -31,7 +31,7 @@ Default outbound tag. the first outbound will be used if empty. #### auto_detect_interface -!!! error "" +!!! quote "" Only supported on Linux, Windows and macOS. @@ -41,7 +41,7 @@ Takes no effect if `outbound.bind_interface` is set. #### override_android_vpn -!!! error "" +!!! quote "" Only supported on Android. @@ -49,7 +49,7 @@ Accept Android VPN as upstream NIC when `auto_detect_interface` enabled. #### default_interface -!!! error "" +!!! quote "" Only supported on Linux, Windows and macOS. @@ -59,7 +59,7 @@ Takes no effect if `auto_detect_interface` is set. #### default_mark -!!! error "" +!!! quote "" Only supported on Linux. diff --git a/docs/configuration/route/index.zh.md b/docs/configuration/route/index.zh.md index c3ef2e54cf..8bef5bea9a 100644 --- a/docs/configuration/route/index.zh.md +++ b/docs/configuration/route/index.zh.md @@ -32,7 +32,7 @@ #### auto_detect_interface -!!! error "" +!!! quote "" 仅支持 Linux、Windows 和 macOS。 @@ -42,7 +42,7 @@ #### override_android_vpn -!!! error "" +!!! quote "" 仅支持 Android。 @@ -50,7 +50,7 @@ #### default_interface -!!! error "" +!!! quote "" 仅支持 Linux、Windows 和 macOS。 @@ -60,7 +60,7 @@ #### default_mark -!!! error "" +!!! quote "" 仅支持 Linux。 diff --git a/docs/configuration/route/rule.md b/docs/configuration/route/rule.md index 3cee478dc3..abbfde6fdc 100644 --- a/docs/configuration/route/rule.md +++ b/docs/configuration/route/rule.md @@ -83,6 +83,12 @@ 1000 ], "clash_mode": "direct", + "wifi_ssid": [ + "My WIFI" + ], + "wifi_bssid": [ + "00:00:00:00:00:00" + ], "invert": false, "outbound": "direct" }, @@ -190,7 +196,7 @@ Match port range. #### process_name -!!! error "" +!!! quote "" Only supported on Linux, Windows, and macOS. @@ -198,7 +204,7 @@ Match process name. #### process_path -!!! error "" +!!! quote "" Only supported on Linux, Windows, and macOS. @@ -210,7 +216,7 @@ Match android package name. #### user -!!! error "" +!!! quote "" Only supported on Linux. @@ -218,7 +224,7 @@ Match user name. #### user_id -!!! error "" +!!! quote "" Only supported on Linux. @@ -228,6 +234,22 @@ Match user id. Match Clash mode. +#### wifi_ssid + +!!! quote "" + + Only supported in graphical clients on Android and iOS. + +Match WiFi SSID. + +#### wifi_bssid + +!!! quote "" + + Only supported in graphical clients on Android and iOS. + +Match WiFi BSSID. + #### invert Invert match result. diff --git a/docs/configuration/route/rule.zh.md b/docs/configuration/route/rule.zh.md index 4a09ed8e64..f4ab7890a7 100644 --- a/docs/configuration/route/rule.zh.md +++ b/docs/configuration/route/rule.zh.md @@ -81,6 +81,12 @@ 1000 ], "clash_mode": "direct", + "wifi_ssid": [ + "My WIFI" + ], + "wifi_bssid": [ + "00:00:00:00:00:00" + ], "invert": false, "outbound": "direct" }, @@ -188,7 +194,7 @@ #### process_name -!!! error "" +!!! quote "" 仅支持 Linux、Windows 和 macOS。 @@ -196,7 +202,7 @@ #### process_path -!!! error "" +!!! quote "" 仅支持 Linux、Windows 和 macOS. @@ -208,7 +214,7 @@ #### user -!!! error "" +!!! quote "" 仅支持 Linux. @@ -216,7 +222,7 @@ #### user_id -!!! error "" +!!! quote "" 仅支持 Linux. @@ -226,6 +232,22 @@ 匹配 Clash 模式。 +#### wifi_ssid + +!!! quote "" + + 仅在 Android 与 iOS 的图形客户端中支持。 + +匹配 WiFi SSID。 + +#### wifi_bssid + +!!! quote "" + + 仅在 Android 与 iOS 的图形客户端中支持。 + +匹配 WiFi BSSID。 + #### invert 反选匹配结果。 diff --git a/docs/configuration/shared/dial.md b/docs/configuration/shared/dial.md index 1f524f0800..8139c7518c 100644 --- a/docs/configuration/shared/dial.md +++ b/docs/configuration/shared/dial.md @@ -41,7 +41,7 @@ The IPv6 address to bind to. #### routing_mark -!!! error "" +!!! quote "" Only supported on Linux. diff --git a/docs/configuration/shared/dial.zh.md b/docs/configuration/shared/dial.zh.md index 62b094f3fb..300e99ff7a 100644 --- a/docs/configuration/shared/dial.zh.md +++ b/docs/configuration/shared/dial.zh.md @@ -44,7 +44,7 @@ #### routing_mark -!!! error "" +!!! quote "" 仅支持 Linux。 diff --git a/docs/configuration/shared/tls.md b/docs/configuration/shared/tls.md index 4d395f7786..9a02bbff2f 100644 --- a/docs/configuration/shared/tls.md +++ b/docs/configuration/shared/tls.md @@ -201,7 +201,7 @@ The path to the server private key, in PEM format. !!! warning "" - uTLS is not included by default, see [Installation](/#installation). + uTLS is not included by default, see [Installation](./#installation). !!! note "" @@ -228,7 +228,7 @@ Chrome fingerprint will be used if empty. !!! warning "" - ECH is not included by default, see [Installation](/#installation). + ECH is not included by default, see [Installation](./#installation). ECH (Encrypted Client Hello) is a TLS extension that allows a client to encrypt the first part of its ClientHello message. @@ -280,7 +280,7 @@ If empty, load from DNS will be attempted. !!! warning "" - ACME is not included by default, see [Installation](/#installation). + ACME is not included by default, see [Installation](./#installation). #### domain @@ -359,11 +359,11 @@ See [DNS01 Challenge Fields](/configuration/shared/dns01_challenge) for details. !!! warning "" - reality server is not included by default, see [Installation](/#installation). + reality server is not included by default, see [Installation](./#installation). !!! warning "" - uTLS, which is required by reality client is not included by default, see [Installation](/#installation). + uTLS, which is required by reality client is not included by default, see [Installation](./#installation). #### handshake diff --git a/docs/configuration/shared/v2ray-transport.md b/docs/configuration/shared/v2ray-transport.md index 418ef28db2..b078bac8af 100644 --- a/docs/configuration/shared/v2ray-transport.md +++ b/docs/configuration/shared/v2ray-transport.md @@ -131,7 +131,7 @@ It needs to be consistent with the server. !!! warning "" - QUIC is not included by default, see [Installation](/#installation). + QUIC is not included by default, see [Installation](./#installation). !!! warning "Difference from v2ray-core" @@ -142,7 +142,7 @@ It needs to be consistent with the server. !!! note "" - standard gRPC has good compatibility but poor performance and is not included by default, see [Installation](/#installation). + standard gRPC has good compatibility but poor performance and is not included by default, see [Installation](./#installation). ```json { diff --git a/docs/contributing/environment.md b/docs/contributing/environment.md deleted file mode 100644 index db2ac91aff..0000000000 --- a/docs/contributing/environment.md +++ /dev/null @@ -1,50 +0,0 @@ -# Development environment - -#### For the documentation - -##### Setup - -You need to configure python3 and pip first. - -```shell -pip install mkdocs-material mkdocs-static-i18n -``` - -##### Run the site locally - -```shell -mkdocs serve -``` - -or - -```shell -python3 -m mkdocs serve -``` - -#### For the project - -By default you have the latest Go installed (currently 1.19), and added `GOPATH/bin` to the PATH environment variable. - -##### Setup - -```shell -make fmt_insalll -make lint_install -``` - -This installs the formatting and lint tools, which can be used via `make fmt` and `make lint`. - -For ProtoBuffer changes, you also need `make proto_install` and `make proto`. - -##### Build binary to the project directory - -```shell -make -``` - -##### Install binary to GOPATH/bin - -```shell -make install -``` \ No newline at end of file diff --git a/docs/contributing/index.md b/docs/contributing/index.md deleted file mode 100644 index d669bda62c..0000000000 --- a/docs/contributing/index.md +++ /dev/null @@ -1,17 +0,0 @@ -# Contributing to sing-box - -An introduction to contributing to the sing-box project. - -The sing-box project welcomes, and depends, on contributions from developers and users in the open source community. -Contributions can be made in a number of ways, a few examples are: - -* Code patches via pull requests -* Documentation improvements -* Bug reports and patch reviews - -### Reporting an Issue? - -Please follow -the [issue template](https://github.com/SagerNet/sing-box/issues/new?assignees=&labels=&template=bug_report.yml) to -submit bugs. Always include **FULL** log content, especially if you don't understand the code that generates it. - diff --git a/docs/contributing/sub-projects.md b/docs/contributing/sub-projects.md deleted file mode 100644 index 3f62a52cd9..0000000000 --- a/docs/contributing/sub-projects.md +++ /dev/null @@ -1,67 +0,0 @@ -The sing-box uses the following projects which also need to be maintained: - -#### sing - -Link: [GitHub repository](https://github.com/SagerNet/sing) - -As a base tool library, there are no dependencies other than `golang.org/x/sys`. - -#### sing-dns - -Link: [GitHub repository](https://github.com/SagerNet/sing-dns) - -Handles DNS lookups and caching. - -#### sing-tun - -Link: [GitHub repository](https://github.com/SagerNet/sing-tun) - -Handle Tun traffic forwarding, configure routing, monitor network and routing. - -This library needs to periodically update its dependency gVisor (according to tags), including checking for changes to -the used parts of the code and updating its usage. If you are involved in maintenance, you also need to check that if it -works or contains memory leaks. - -#### sing-shadowsocks - -Link: [GitHub repository](https://github.com/SagerNet/sing-shadowsocks) - -Provides Shadowsocks client and server - -#### sing-vmess - -Link: [GitHub repository](https://github.com/SagerNet/sing-vmess) - -Provides VMess client and server - -#### netlink - -Link: [GitHub repository](https://github.com/SagerNet/netlink) - -Fork of `vishvananda/netlink`, with some rule fixes. - -The library needs to be updated with the upstream. - -#### quic-go - -Link: [GitHub repository](https://github.com/SagerNet/quic-go) - -Fork of `lucas-clemente/quic-go` and `HyNetwork/quic-go`, contains quic flow control and other fixes used by Hysteria. - -Since the author of Hysteria does not follow the upstream updates in time, and the provided fork needs to use replace, -we need to do this. - -The library needs to be updated with the upstream. - -#### smux - -Link: [GitHub repository](https://github.com/SagerNet/smux) - -Fork of `xtaci/smux` - -Modify the code to support the writev it uses internally and unify the buffer pool, which prevents it from allocating -64k buffers for per connection and improves performance. - -Upstream doesn't seem to be updated anymore, maybe a replacement is needed. - -Note: while yamux is still actively maintained and better known, it seems to be less performant. diff --git a/docs/deprecated.md b/docs/deprecated.md index 521a399490..91a3b25e58 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -1,5 +1,11 @@ +--- +icon: material/delete-alert +--- + # Deprecated Feature List +### 1.6.0 + The following features will be marked deprecated in 1.5.0 and removed entirely in 1.6.0. #### ShadowsocksR diff --git a/docs/deprecated.zh.md b/docs/deprecated.zh.md new file mode 100644 index 0000000000..f1125565f9 --- /dev/null +++ b/docs/deprecated.zh.md @@ -0,0 +1,17 @@ +--- +icon: material/delete-alert +--- + +# 废弃功能列表 + +### 1.6.0 + +下列功能已在 1.5.0 中标记为已弃用,并在 1.6.0 中完全删除。 + +#### ShadowsocksR + +ShadowsocksR 支持从未默认启用,自从常用的黑产代理销售面板停止使用该协议,继续维护它是没有意义的。 + +#### Proxy Protocol + +Proxy Protocol 支持由 Pull Request 添加,存在问题且仅由 HTTP 多路复用器(如 nginx)的后端使用,具有侵入性,对于代理目的毫无意义。 diff --git a/docs/examples/clash-api.md b/docs/examples/clash-api.md deleted file mode 100644 index ca5a01338f..0000000000 --- a/docs/examples/clash-api.md +++ /dev/null @@ -1,52 +0,0 @@ -```json -{ - "dns": { - "rules": [ - { - "domain": [ - "clash.razord.top", - "yacd.haishan.me" - ], - "server": "local" - }, - { - "clash_mode": "direct", - "server": "local" - } - ] - }, - "outbounds": [ - { - "type": "selector", - "tag": "default", - "outbounds": [ - "proxy-a", - "proxy-b" - ] - } - ], - "route": { - "rules": [ - { - "clash_mode": "direct", - "outbound": "direct" - }, - { - "domain": [ - "clash.razord.top", - "yacd.haishan.me" - ], - "outbound": "direct" - } - ], - "final": "default" - }, - "experimental": { - "clash_api": { - "external_controller": "127.0.0.1:9090", - "store_selected": true - } - } -} - -``` \ No newline at end of file diff --git a/docs/examples/dns-hijack.md b/docs/examples/dns-hijack.md deleted file mode 100644 index db9e86b808..0000000000 --- a/docs/examples/dns-hijack.md +++ /dev/null @@ -1,65 +0,0 @@ -#### Sniff Mode - -```json -{ - "inbounds": [ - { - "type": "tun", - "inet4_address": "172.19.0.1/30", - "auto_route": true, - "sniff": true // required - } - ], - "outbounds": [ - { - "type": "direct" - }, - { - "type": "dns", - "tag": "dns-out" - } - ], - "route": { - "rules": [ - { - "protocol": "dns", - "outbound": "dns-out" - } - ], - "auto_detect_interface": true - } -} -``` - -#### Port Mode - -```json -{ - "inbounds": [ - { - "type": "tun", - "inet4_address": "172.19.0.1/30", - "auto_route": true, - "sniff": true // not required - } - ], - "outbounds": [ - { - "type": "direct" - }, - { - "type": "dns", - "tag": "dns-out" - } - ], - "route": { - "rules": [ - { - "port": 53, - "outbound": "dns-out" - } - ], - "auto_detect_interface": true - } -} -``` \ No newline at end of file diff --git a/docs/examples/dns-hijack.zh.md b/docs/examples/dns-hijack.zh.md deleted file mode 100644 index 2e75b13a7b..0000000000 --- a/docs/examples/dns-hijack.zh.md +++ /dev/null @@ -1,65 +0,0 @@ -#### 探测模式 - -```json -{ - "inbounds": [ - { - "type": "tun", - "inet4_address": "172.19.0.1/30", - "auto_route": true, - "sniff": true // 必须 - } - ], - "outbounds": [ - { - "type": "direct" - }, - { - "type": "dns", - "tag": "dns-out" - } - ], - "route": { - "rules": [ - { - "protocol": "dns", - "outbound": "dns-out" - } - ], - "auto_detect_interface": true - } -} -``` - -#### 端口模式 - -```json -{ - "inbounds": [ - { - "type": "tun", - "inet4_address": "172.19.0.1/30", - "auto_route": true, - "sniff": true // 非必须 - } - ], - "outbounds": [ - { - "type": "direct" - }, - { - "type": "dns", - "tag": "dns-out" - } - ], - "route": { - "rules": [ - { - "port": 53, - "outbound": "dns-out" - } - ], - "auto_detect_interface": true - } -} -``` \ No newline at end of file diff --git a/docs/examples/fakeip.md b/docs/examples/fakeip.md deleted file mode 100644 index 21407eced4..0000000000 --- a/docs/examples/fakeip.md +++ /dev/null @@ -1,106 +0,0 @@ -```json -{ - "dns": { - "servers": [ - { - "tag": "google", - "address": "tls://8.8.8.8" - }, - { - "tag": "local", - "address": "223.5.5.5", - "detour": "direct" - }, - { - "tag": "remote", - "address": "fakeip" - }, - { - "tag": "block", - "address": "rcode://success" - } - ], - "rules": [ - { - "geosite": "category-ads-all", - "server": "block", - "disable_cache": true - }, - { - "outbound": "any", - "server": "local" - }, - { - "geosite": "cn", - "server": "local" - }, - { - "query_type": [ - "A", - "AAAA" - ], - "server": "remote" - } - ], - "fakeip": { - "enabled": true, - "inet4_range": "198.18.0.0/15", - "inet6_range": "fc00::/18" - }, - "independent_cache": true, - "strategy": "ipv4_only" - }, - "inbounds": [ - { - "type": "tun", - "inet4_address": "172.19.0.1/30", - "auto_route": true, - "sniff": true, - "domain_strategy": "ipv4_only" // remove this line if you want to resolve the domain remotely (if the server is not sing-box, UDP may not work due to wrong behavior). - } - ], - "outbounds": [ - { - "type": "shadowsocks", - "tag": "proxy", - "server": "mydomain.com", - "server_port": 8080, - "method": "2022-blake3-aes-128-gcm", - "password": "8JCsPssfgS8tiRwiMlhARg==" - }, - { - "type": "direct", - "tag": "direct" - }, - { - "type": "block", - "tag": "block" - }, - { - "type": "dns", - "tag": "dns-out" - } - ], - "route": { - "rules": [ - { - "protocol": "dns", - "outbound": "dns-out" - }, - { - "geosite": "cn", - "geoip": [ - "private", - "cn" - ], - "outbound": "direct" - }, - { - "geosite": "category-ads-all", - "outbound": "block" - } - ], - "auto_detect_interface": true - } -} -``` \ No newline at end of file diff --git a/docs/examples/fakeip.zh.md b/docs/examples/fakeip.zh.md deleted file mode 100644 index 947ce387ae..0000000000 --- a/docs/examples/fakeip.zh.md +++ /dev/null @@ -1,106 +0,0 @@ -```json -{ - "dns": { - "servers": [ - { - "tag": "google", - "address": "tls://8.8.8.8" - }, - { - "tag": "local", - "address": "223.5.5.5", - "detour": "direct" - }, - { - "tag": "remote", - "address": "fakeip" - }, - { - "tag": "block", - "address": "rcode://success" - } - ], - "rules": [ - { - "geosite": "category-ads-all", - "server": "block", - "disable_cache": true - }, - { - "outbound": "any", - "server": "local" - }, - { - "geosite": "cn", - "server": "local" - }, - { - "query_type": [ - "A", - "AAAA" - ], - "server": "remote" - } - ], - "fakeip": { - "enabled": true, - "inet4_range": "198.18.0.0/15", - "inet6_range": "fc00::/18" - }, - "independent_cache": true, - "strategy": "ipv4_only" - }, - "inbounds": [ - { - "type": "tun", - "inet4_address": "172.19.0.1/30", - "auto_route": true, - "sniff": true, - "domain_strategy": "ipv4_only" // 如果您想在远程解析域,删除此行 (如果服务器程序不为 sing-box,可能由于错误的行为导致 UDP 无法使用)。 - } - ], - "outbounds": [ - { - "type": "shadowsocks", - "tag": "proxy", - "server": "mydomain.com", - "server_port": 8080, - "method": "2022-blake3-aes-128-gcm", - "password": "8JCsPssfgS8tiRwiMlhARg==" - }, - { - "type": "direct", - "tag": "direct" - }, - { - "type": "block", - "tag": "block" - }, - { - "type": "dns", - "tag": "dns-out" - } - ], - "route": { - "rules": [ - { - "protocol": "dns", - "outbound": "dns-out" - }, - { - "geosite": "cn", - "geoip": [ - "private", - "cn" - ], - "outbound": "direct" - }, - { - "geosite": "category-ads-all", - "outbound": "block" - } - ], - "auto_detect_interface": true - } -} -``` \ No newline at end of file diff --git a/docs/examples/index.md b/docs/examples/index.md deleted file mode 100644 index 6bbab74145..0000000000 --- a/docs/examples/index.md +++ /dev/null @@ -1,11 +0,0 @@ -# Examples - -Configuration examples for sing-box. - -* [Linux Server Installation](./linux-server-installation) -* [Tun](./tun) -* [DNS Hijack](./dns-hijack.md) -* [Shadowsocks](./shadowsocks) -* [ShadowTLS](./shadowtls) -* [Clash API](./clash-api) -* [FakeIP](./fakeip) diff --git a/docs/examples/index.zh.md b/docs/examples/index.zh.md deleted file mode 100644 index 7528e5d438..0000000000 --- a/docs/examples/index.zh.md +++ /dev/null @@ -1,11 +0,0 @@ -# 示例 - -sing-box 的配置示例。 - -* [Linux 服务器安装](./linux-server-installation) -* [Tun](./tun) -* [DNS 劫持](./dns-hijack.md) -* [Shadowsocks](./shadowsocks) -* [ShadowTLS](./shadowtls) -* [Clash API](./clash-api) -* [FakeIP](./fakeip) diff --git a/docs/examples/linux-server-installation.md b/docs/examples/linux-server-installation.md deleted file mode 100644 index a785b130b7..0000000000 --- a/docs/examples/linux-server-installation.md +++ /dev/null @@ -1,38 +0,0 @@ -#### Requirements - -* Linux & Systemd -* Git -* C compiler environment - -#### Install - -```shell -git clone -b main https://github.com/SagerNet/sing-box -cd sing-box -./release/local/install_go.sh # skip if you have golang already installed -./release/local/install.sh -``` - -Edit configuration file in `/usr/local/etc/sing-box/config.json` - -```shell -./release/local/enable.sh -``` - -#### Update - -```shell -./release/local/update.sh -``` - -#### Other commands - -| Operation | Command | -|-----------|-----------------------------------------------| -| Start | `sudo systemctl start sing-box` | -| Stop | `sudo systemctl stop sing-box` | -| Kill | `sudo systemctl kill sing-box` | -| Restart | `sudo systemctl restart sing-box` | -| Logs | `sudo journalctl -u sing-box --output cat -e` | -| New Logs | `sudo journalctl -u sing-box --output cat -f` | -| Uninstall | `./release/local/uninstall.sh` | \ No newline at end of file diff --git a/docs/examples/linux-server-installation.zh.md b/docs/examples/linux-server-installation.zh.md deleted file mode 100644 index 91d908d67d..0000000000 --- a/docs/examples/linux-server-installation.zh.md +++ /dev/null @@ -1,38 +0,0 @@ -#### 依赖 - -* Linux & Systemd -* Git -* C 编译器环境 - -#### 安装 - -```shell -git clone -b main https://github.com/SagerNet/sing-box -cd sing-box -./release/local/install_go.sh # 如果已安装 golang 则跳过 -./release/local/install.sh -``` - -编辑配置文件 `/usr/local/etc/sing-box/config.json` - -```shell -./release/local/enable.sh -``` - -#### 更新 - -```shell -./release/local/update.sh -``` - -#### 其他命令 - -| 操作 | 命令 | -|------|-----------------------------------------------| -| 启动 | `sudo systemctl start sing-box` | -| 停止 | `sudo systemctl stop sing-box` | -| 强制停止 | `sudo systemctl kill sing-box` | -| 重启 | `sudo systemctl restart sing-box` | -| 查看日志 | `sudo journalctl -u sing-box --output cat -e` | -| 实时日志 | `sudo journalctl -u sing-box --output cat -f` | -| 卸载 | `./release/local/uninstall.sh` | \ No newline at end of file diff --git a/docs/examples/shadowsocks.md b/docs/examples/shadowsocks.md deleted file mode 100644 index 7e002fab4d..0000000000 --- a/docs/examples/shadowsocks.md +++ /dev/null @@ -1,163 +0,0 @@ -# Shadowsocks - -!!! warning "" - - For censorship bypass usage in China, we recommend using UDP over TCP and disabling UDP on the server. - -## Single User - -#### Server - -```json -{ - "inbounds": [ - { - "type": "shadowsocks", - "listen": "::", - "listen_port": 8080, - "network": "tcp", - "method": "2022-blake3-aes-128-gcm", - "password": "8JCsPssfgS8tiRwiMlhARg==" - } - ] -} -``` - -#### Client - -```json -{ - "inbounds": [ - { - "type": "mixed", - "listen": "::", - "listen_port": 2080 - } - ], - "outbounds": [ - { - "type": "shadowsocks", - "server": "127.0.0.1", - "server_port": 8080, - "method": "2022-blake3-aes-128-gcm", - "password": "8JCsPssfgS8tiRwiMlhARg==", - "udp_over_tcp": true - } - ] -} - -``` - -## Multiple Users - -#### Server - -```json -{ - "inbounds": [ - { - "type": "shadowsocks", - "listen": "::", - "listen_port": 8080, - "method": "2022-blake3-aes-128-gcm", - "password": "8JCsPssfgS8tiRwiMlhARg==", - "users": [ - { - "name": "sekai", - "password": "BXYxVUXJ9NgF7c7KPLQjkg==" - } - ] - } - ] -} -``` - -#### Client - -```json -{ - "inbounds": [ - { - "type": "mixed", - "listen": "::", - "listen_port": 2080 - } - ], - "outbounds": [ - { - "type": "shadowsocks", - "server": "127.0.0.1", - "server_port": 8080, - "method": "2022-blake3-aes-128-gcm", - "password": "8JCsPssfgS8tiRwiMlhARg==:BXYxVUXJ9NgF7c7KPLQjkg==" - } - ] -} - -``` - -## Relay - -#### Server - -```json -{ - "inbounds": [ - { - "type": "shadowsocks", - "listen": "::", - "listen_port": 8080, - "method": "2022-blake3-aes-128-gcm", - "password": "8JCsPssfgS8tiRwiMlhARg==" - } - ] -} -``` - -#### Relay - -```json -{ - "inbounds": [ - { - "type": "shadowsocks", - "listen": "::", - "listen_port": 8081, - "method": "2022-blake3-aes-128-gcm", - "password": "BXYxVUXJ9NgF7c7KPLQjkg==", - "destinations": [ - { - "name": "my_server", - "password": "8JCsPssfgS8tiRwiMlhARg==", - "server": "127.0.0.1", - "server_port": 8080 - } - ] - } - ] -} -``` - -#### Client - -```json -{ - "inbounds": [ - { - "type": "mixed", - "listen": "::", - "listen_port": 2080 - } - ], - "outbounds": [ - { - "type": "shadowsocks", - "server": "127.0.0.1", - "server_port": 8081, - "method": "2022-blake3-aes-128-gcm", - "password": "8JCsPssfgS8tiRwiMlhARg==:BXYxVUXJ9NgF7c7KPLQjkg==" - } - ] -} - -``` \ No newline at end of file diff --git a/docs/examples/shadowtls.md b/docs/examples/shadowtls.md deleted file mode 100644 index 352a305846..0000000000 --- a/docs/examples/shadowtls.md +++ /dev/null @@ -1,70 +0,0 @@ -#### Server - -```json -{ - "inbounds": [ - { - "type": "shadowtls", - "listen": "::", - "listen_port": 4443, - "version": 3, - "users": [ - { - "name": "sekai", - "password": "8JCsPssfgS8tiRwiMlhARg==" - } - ], - "handshake": { - "server": "google.com", - "server_port": 443 - }, - "detour": "shadowsocks-in" - }, - { - "type": "shadowsocks", - "tag": "shadowsocks-in", - "listen": "127.0.0.1", - "network": "tcp", - "method": "2022-blake3-aes-128-gcm", - "password": "8JCsPssfgS8tiRwiMlhARg==" - } - ] -} -``` - -#### Client - -```json -{ - "outbounds": [ - { - "type": "shadowsocks", - "method": "2022-blake3-aes-128-gcm", - "password": "8JCsPssfgS8tiRwiMlhARg==", - "detour": "shadowtls-out", - "multiplex": { - "enabled": true, - "max_connections": 4, - "min_streams": 4 - } - // or "udp_over_tcp": true - }, - { - "type": "shadowtls", - "tag": "shadowtls-out", - "server": "127.0.0.1", - "server_port": 4443, - "version": 3, - "password": "8JCsPssfgS8tiRwiMlhARg==", - "tls": { - "enabled": true, - "server_name": "google.com", - "utls": { - "enabled": true, - "fingerprint": "chrome" - } - } - } - ] -} -``` diff --git a/docs/examples/tun.md b/docs/examples/tun.md deleted file mode 100644 index 8671ef76a6..0000000000 --- a/docs/examples/tun.md +++ /dev/null @@ -1,89 +0,0 @@ -```json -{ - "dns": { - "servers": [ - { - "tag": "google", - "address": "tls://8.8.8.8" - }, - { - "tag": "local", - "address": "223.5.5.5", - "detour": "direct" - }, - { - "tag": "block", - "address": "rcode://success" - } - ], - "rules": [ - { - "geosite": "category-ads-all", - "server": "block", - "disable_cache": true - }, - { - "outbound": "any", - "server": "local" - }, - { - "geosite": "cn", - "server": "local" - } - ], - "strategy": "ipv4_only" - }, - "inbounds": [ - { - "type": "tun", - "inet4_address": "172.19.0.1/30", - "auto_route": true, - "strict_route": false, - "sniff": true - } - ], - "outbounds": [ - { - "type": "shadowsocks", - "tag": "proxy", - "server": "mydomain.com", - "server_port": 8080, - "method": "2022-blake3-aes-128-gcm", - "password": "8JCsPssfgS8tiRwiMlhARg==" - }, - { - "type": "direct", - "tag": "direct" - }, - { - "type": "block", - "tag": "block" - }, - { - "type": "dns", - "tag": "dns-out" - } - ], - "route": { - "rules": [ - { - "protocol": "dns", - "outbound": "dns-out" - }, - { - "geosite": "cn", - "geoip": [ - "private", - "cn" - ], - "outbound": "direct" - }, - { - "geosite": "category-ads-all", - "outbound": "block" - } - ], - "auto_detect_interface": true - } -} -``` \ No newline at end of file diff --git a/docs/faq/fakeip.md b/docs/faq/fakeip.md deleted file mode 100644 index 59d9e730d9..0000000000 --- a/docs/faq/fakeip.md +++ /dev/null @@ -1,19 +0,0 @@ -# FakeIP - -FakeIP refers to a type of behavior in a program that simultaneously hijacks both DNS and connection requests. It -responds to DNS requests with virtual results and restores mapping when accepting connections. - -#### Advantage - -* - -#### Limitation - -* Its mechanism breaks applications that depend on returning correct remote addresses. -* Only A and AAAA (IP) requests are supported, which may break applications that rely on other requests. - -#### Recommendation - -* Enable `dns.independent_cache` unless you always resolve FakeIP domains remotely. -* If using tun, make sure FakeIP ranges is included in the tun's routes. -* Enable `experimental.clash_api.store_fakeip` to persist FakeIP records, or use `dns.rules.rewrite_ttl` to avoid losing records after program restart in DNS cached environments. diff --git a/docs/faq/fakeip.zh.md b/docs/faq/fakeip.zh.md deleted file mode 100644 index 3ab77d2c6a..0000000000 --- a/docs/faq/fakeip.zh.md +++ /dev/null @@ -1,18 +0,0 @@ -# FakeIP - -FakeIP 是指同时劫持 DNS 和连接请求的程序中的一种行为。它通过虚拟结果响应 DNS 请求,在接受连接时恢复映射。 - -#### 优点 - -* - -#### 限制 - -* 它的机制会破坏依赖于返回正确远程地址的应用程序。 -* 仅支持 A 和 AAAA(IP)请求,这可能会破坏依赖于其他请求的应用程序。 - -#### 建议 - -* 启用 `dns.independent_cache` 除非您始终远程解析 FakeIP 域。 -* 如果使用 tun,请确保 tun 路由中包含 FakeIP 地址范围。 -* 启用 `experimental.clash_api.store_fakeip` 以持久化 FakeIP 记录,或者使用 `dns.rules.rewrite_ttl` 避免程序重启后在 DNS 被缓存的环境中丢失记录。 diff --git a/docs/faq/index.md b/docs/faq/index.md deleted file mode 100644 index c7ce30ad65..0000000000 --- a/docs/faq/index.md +++ /dev/null @@ -1,58 +0,0 @@ -# Frequently Asked Questions (FAQ) - -## Design - -#### Why does sing-box not have feature X? - -Every program contains novel features and omits someone's favorite feature. sing-box is designed with an eye to the -needs of performance, lightweight, usability, modularity, and code quality. Your favorite feature may be missing because -it doesn't fit, because it compromises performance or design clarity, or because it's a bad idea. - -If it bothers you that sing-box is missing feature X, please forgive us and investigate the features that sing-box does -have. You might find that they compensate in interesting ways for the lack of X. - -#### Naive outbound - -NaïveProxy's main function is chromium's network stack, and it makes no sense to implement only its transport protocol. - -#### Protocol combinations - -The "underlying transport" in v2ray-core is actually a combination of a number of proprietary protocols and uses the -names of their upstream protocols, resulting in a great deal of Linguistic corruption. - -For example, Trojan with v2ray's proprietary gRPC protocol, called Trojan gRPC by the v2ray community, is not actually a -protocol and has no role outside of abusing CDNs. - -## Tun - -#### What is tun? - -tun is a virtual network device in unix systems, and in windows there is wintun developed by WireGuard as an -alternative. The tun module of sing-box includes traffic processing, automatic routing, and network device listening, -and is mainly used as a transparent proxy. - -#### How is it different from system proxy? - -System proxy usually only supports TCP and is not accepted by all applications, but tun can handle all traffic. - -#### How is it different from traditional transparent proxy? - -They are usually only supported under Linux and require manipulation of firewalls like iptables, while tun only modifies -the routing table. - -The tproxy UDP is considered to have poor performance due to the need to create a new connection every write back in -v2ray and clash, but it is optimized in sing-box so you can still use it if needed. - -#### How does it handle DNS? - -In traditional transparent proxies, it is usually necessary to manually hijack port 53 to the DNS proxy server, while -tun is more flexible. - -sing-box's `auto_route` will hijack all DNS requests except on [macOS and Android](./known-issues#dns). - -You need to manually configure how to handle tun hijacked DNS traffic, see [Hijack DNS](/examples/dns-hijack). - -#### Why I can't use it with other local proxies (e.g. via socks)? - -Tun will hijack all traffic, including other proxy applications. In order to make tun work with other applications, you -need to create an inbound to proxy traffic from other applications or make them bypass the route. \ No newline at end of file diff --git a/docs/faq/index.zh.md b/docs/faq/index.zh.md deleted file mode 100644 index 557c53c7fa..0000000000 --- a/docs/faq/index.zh.md +++ /dev/null @@ -1,50 +0,0 @@ -# 常见问题 - -## 设计 - -#### 为什么 sing-box 没有功能 X? - -每个程序都包含新颖的功能并省略了某人最喜欢的功能。 sing-box 的设计着眼于高性能、轻量、可用性、模块化和代码质量的需求。 -您最喜欢的功能可能会丢失,因为它不适合,因为它会影响性能或设计清晰度,或者因为它是一个坏主意。 - -如果 sing-box 缺少功能 X 让您感到困扰,请原谅我们并调查 sing-box 确实有的功能。 您可能会发现它们以有趣的方式弥补了 X 的缺失。 - -#### Naive 出站 - -NaïveProxy 的主要功能是 chromium 的网络栈,仅实现它的传输协议是舍本逐末的。 - -#### 协议组合 - -v2ray-core 中的 "底层传输协议" 实际上是一些专有协议的组合,并使用其上游协议的名称,造成了大量的语言腐败。 - -例如,v2ray 社区将 v2ray 专有的 gRPC 协议称为 Trojan gRPC,其实并不是一个 协议,在滥用 CDN 之外没有任何作用。 - -## Tun - -#### 什么是 tun? - -tun 是 unix 系统中的虚拟网络设备,在 windows 中有 WireGuard 开发的 wintun 作为替代。 -sing-box 的 tun 模块包括流量处理、自动路由和网络设备监听,并主要用作透明代理。 - -#### 它与系统代理有什么不同? - -系统代理通常只支持 TCP,且不被所有应用程序接受,但 tun 可以处理所有流量。 - -#### 它与传统的透明代理有什么不同? - -它们通常仅支持 Linux,并且需要操作防火墙像 iptables,而 tun 仅修改路由表。 - -tproxy UDP 被认为性能很差,因为在 v2ray 和 clash 中每次回写都需要创建一个新连接,但 sing-box 进行了优化,因此您仍然可以在需要时使用它。 - -#### 它如何处理 DNS? - -在传统的透明代理中,通常需要手动劫持 53 端口到 DNS 代理服务器,而 tun 更灵活。 - -sing-box 的 `auto_route` 将劫持所有 DNS 请求,除了 [特殊情况](./known-issues#dns)。 - -您需要手动配置以处理 tun 劫持的 DNS 流量,请参阅 [DNS劫持](/zh/examples/dns-hijack)。 - -#### 为什么我不能将它与其他本地代理一起使用(例如通过 socks)? - -tun 将劫持所有流量,包括其他代理应用程序。 -为了使 tun 与其他应用程序一起工作,您需要创建入站以代理来自其他程序的流量或让它们绕过路由。 \ No newline at end of file diff --git a/docs/faq/known-issues.md b/docs/faq/known-issues.md deleted file mode 100644 index ac6892e1ee..0000000000 --- a/docs/faq/known-issues.md +++ /dev/null @@ -1,20 +0,0 @@ -#### DNS - -##### on macOS - -`auto-route` cannot automatically hijack DNS requests sent to the LAN, so it's need to manually set DNS to servers on -the public internet. - -##### on Android - -`auto-route` cannot automatically hijack DNS requests when Android's `Private DNS` enabled or `strict_route` disabled. - -#### System proxy - -##### on Linux - -Usually only browsers and GNOME applications accept GNOME proxy settings. - -##### on Android - -With the system proxy enabled, some applications will error out (usually from China). \ No newline at end of file diff --git a/docs/faq/known-issues.zh.md b/docs/faq/known-issues.zh.md deleted file mode 100644 index 1d25e723cb..0000000000 --- a/docs/faq/known-issues.zh.md +++ /dev/null @@ -1,19 +0,0 @@ -#### DNS - -##### macOS - -`auto-route` 无法自动劫持发往局域网的 DNS 请求,需要手动设置位于公网的 DNS 服务器。 - -##### Android - -`auto-route` 无法自动劫持 DNS 请求如果 `私人 DNS` 开启或 `strict_route` 禁用。 - -#### 系统代理 - -##### Linux - -通常只有浏览器和 GNOME 应用程序接受 GNOME 代理设置。 - -##### Android - -启用系统代理后,某些应用程序会出错(通常来自中国)。 diff --git a/docs/features.md b/docs/features.md deleted file mode 100644 index 84185bca2b..0000000000 --- a/docs/features.md +++ /dev/null @@ -1,121 +0,0 @@ -#### Server - -| Feature | v2ray-core | clash | -|------------------------------------------------------------|------------|-------| -| Direct inbound | ✔ | X | -| SOCKS4a inbound | ✔ | X | -| Mixed (http/socks) inbound | X | ✔ | -| Shadowsocks AEAD 2022 single-user/multi-user/relay inbound | X | X | -| VMess/Trojan inbound | ✔ | X | -| Naive/Hysteria inbound | X | X | -| Resolve incoming domain names using custom policy | X | X | -| Set system proxy on Windows/Linux/macOS/Android | X | X | -| TLS certificate auto reload | X | X | -| TLS ACME certificate issuer | X | X | - -#### Client - -| Feature | v2ray-core | clash | -|--------------------------------------------------------|------------------------------------|----------| -| Set upstream client (proxy chain) | TCP only, and has poor performance | TCP only | -| Bind to network interface | Linux only | ✔ | -| Custom dns strategy for resolving server address | X | X | -| Fast fallback (RFC 6555) support for connect to server | X | X | -| SOCKS4/4a outbound | added by me | X | -| Shadowsocks AEAD 2022 outbound | X | X | -| Shadowsocks UDP over TCP | X | X | -| Multiplex (smux/yamux) | mux.cool | X | -| Tor/WireGuard/Hysteria outbound | X | X | -| Selector outbound and Clash API | X | ✔ | - -#### Sniffing - -| Protocol | v2ray-core | clash-premium | -|------------------|-------------|---------------| -| HTTP Host | ✔ | X | -| QUIC ClientHello | added by me | added by me | -| STUN | X | X | - -| Feature | v2ray-core | clash-premium | -|-----------------------------------------|---------------------------|---------------| -| For routing only | added by me | X | -| No performance impact (like TCP splice) | no general splice support | X | -| Set separately for each server | ✔ | X | - -#### Routing - -| Feature | v2ray-core | clash-premium | -|----------------------------|------------|---------------| -| Auto detect interface | X | tun only | -| Set default interface name | X | tun only | -| Set default firewall mark | X | X | - -#### Routing Rule - -| Rule | v2ray-core | clash | -|----------------------|----------------------------|-------| -| Inbound | ✔ | X | -| IP Version | X | X | -| User from inbound | vmess and shadowsocks only | X | -| Sniffed protocol | ✔ | X | -| GeoSite | ✔ | X | -| Process name | X | ✔ | -| Android package name | X | X | -| Linux user/user id | X | X | -| Invert rule | X | X | -| Logical rule | X | X | - -#### DNS - -| Feature | v2ray-core | clash | -|------------------------------------|-------------|-------| -| DNS proxy | A/AAAA only | ✔ | -| DNS cache | A/AAAA only | X | -| DNS routing | X | X | -| DNS Over QUIC | ✔ | X | -| DNS Over HTTP3 | X | X | -| Fake dns response with custom code | X | X | - -#### Tun - -| Feature | clash-premium | -|-------------------------------------------|---------------| -| Full IPv6 support | X | -| Auto route on Linux/Windows/macOS/Android | ✔ | -| Embed windows driver | X | -| Custom address/mtu | X | -| Limit uid (Linux) in routing | X | -| Limit android user in routing | X | -| Limit android package in routing | X | - -#### Memory usage - -| GeoSite code | sing-box | v2ray-core | -|-------------------|----------|------------| -| cn | 17.8M | 140.3M | -| cn (Loyalsoldier) | 74.3M | 246.7M | - -#### Benchmark - -##### Shadowsocks - -| / | none | aes-128-gcm | 2022-blake3-aes-128-gcm | -|------------------------------------|:---------:|:-----------:|:-----------------------:| -| v2ray-core (5.0.7) | 13.0 Gbps | 5.02 Gbps | / | -| shadowsocks-rust (v1.15.0-alpha.5) | 10.7 Gbps | / | 9.36 Gbps | -| sing-box | 29.0 Gbps | / | 11.8 Gbps | - -##### VMess - -| / | TCP | HTTP | H2 TLS | WebSocket TLS | gRPC TLS | -|--------------------|:---------:|:---------:|:---------:|:-------------:|:---------:| -| v2ray-core (5.1.0) | 7.86 GBps | 2.86 Gbps | 1.83 Gbps | 2.36 Gbps | 2.43 Gbps | -| sing-box | 7.96 Gbps | 8.09 Gbps | 6.11 Gbps | 8.02 Gbps | 6.35 Gbps | - -#### License - -| / | License | -|------------|-----------------------------------| -| sing-box | GPLv3 or later (Full open-source) | -| v2ray-core | MIT (Full open-source) | -| clash | GPLv3 | \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 2c34509c24..2af7222e7b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,7 +2,7 @@ description: Welcome to the wiki page for the sing-box project. --- -# Home +# :material-home: Home Welcome to the wiki page for the sing-box project. diff --git a/docs/index.zh.md b/docs/index.zh.md index 2453bb73b4..72877118f3 100644 --- a/docs/index.zh.md +++ b/docs/index.zh.md @@ -2,7 +2,7 @@ description: 欢迎来到该 sing-box 项目的文档页。 --- -# 开始 +# :material-home: 开始 欢迎来到该 sing-box 项目的文档页。 diff --git a/docs/installation/build-from-source.md b/docs/installation/build-from-source.md new file mode 100644 index 0000000000..0ac7169a14 --- /dev/null +++ b/docs/installation/build-from-source.md @@ -0,0 +1,63 @@ +--- +icon: material/file-code +--- + +# Build from source + +## :material-graph: Requirements + +Before sing-box 1.4.0: + +* Go 1.18.5 - 1.20.x + +Since sing-box 1.4.0: + +* Go 1.18.5 - ~ +* Go 1.20.0 - ~ if `with_quic` tag enabled + +You can download and install Go from: https://go.dev/doc/install, latest version is recommended. + +## :material-fast-forward: Simple Build + +```bash +make +``` + +Or build and install binary to `GOBIN`: + +```bash +make install +``` + +## :material-cog: Custom Build + +```bash +TAGS="tag_a tag_b" make +``` + +or + +```bash +go build -tags "tag_a tag_b" ./cmd/sing-box +``` + +## :material-folder-settings: Build Tags + +| Build Tag | Enabled by default | Description | +|------------------------------------|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `with_quic` | ✔ | Build with QUIC support, see [QUIC and HTTP3 DNS transports](/configuration/dns/server), [Naive inbound](/configuration/inbound/naive), [Hysteria Inbound](/configuration/inbound/hysteria), [Hysteria Outbound](/configuration/outbound/hysteria) and [V2Ray Transport#QUIC](/configuration/shared/v2ray-transport#quic). | +| `with_grpc` | ✖️ | Build with standard gRPC support, see [V2Ray Transport#gRPC](/configuration/shared/v2ray-transport#grpc). | +| `with_dhcp` | ✔ | Build with DHCP support, see [DHCP DNS transport](/configuration/dns/server). | +| `with_wireguard` | ✔ | Build with WireGuard support, see [WireGuard outbound](/configuration/outbound/wireguard). | +| `with_ech` | ✔ | Build with TLS ECH extension support for TLS outbound, see [TLS](/configuration/shared/tls#ech). | +| `with_utls` | ✔ | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](/configuration/shared/tls#utls). | +| `with_reality_server` | ✔ | Build with reality TLS server support, see [TLS](/configuration/shared/tls). | +| `with_acme` | ✔ | Build with ACME TLS certificate issuer support, see [TLS](/configuration/shared/tls). | +| `with_clash_api` | ✔ | Build with Clash API support, see [Experimental](/configuration/experimental#clash-api-fields). | +| `with_v2ray_api` | ✖️ | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). | +| `with_gvisor` | ✔ | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). | +| `with_embedded_tor` (CGO required) | ✖️ | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor). | +| `with_lwip` (CGO required) | ✖️ | Build with LWIP Tun stack support, see [Tun inbound](/configuration/inbound/tun#stack). | + + +It is not recommended to change the default build tag list unless you really know what you are adding. diff --git a/docs/installation/clients/sfa.md b/docs/installation/clients/sfa.md deleted file mode 100644 index 54b4f3e553..0000000000 --- a/docs/installation/clients/sfa.md +++ /dev/null @@ -1,23 +0,0 @@ -# SFA - -Experimental Android client for sing-box. - -#### Requirements - -* Android 5.0+ - -#### Download - -* [Play Store](https://play.google.com/store/apps/details?id=io.nekohasekai.sfa) -* [Github Releases](https://github.com/SagerNet/sing-box/releases) - -#### Note - -* User Agent in remote profile request is `SFA/$version ($version_code; sing-box $sing_box_version)` -* The working directory is located at `/sdcard/Android/data/io.nekohasekai.sfa/files` (External files directory) -* Crash logs is located in `$working_directory/stderr.log` - -#### Privacy policy - -* SFA did not collect or share personal data. -* The data generated by the software is always on your device. diff --git a/docs/installation/clients/sfa.zh.md b/docs/installation/clients/sfa.zh.md deleted file mode 100644 index 7dfe63352e..0000000000 --- a/docs/installation/clients/sfa.zh.md +++ /dev/null @@ -1,23 +0,0 @@ -# SFA - -实验性的 Android sing-box 客户端。 - -#### 要求 - -* Android 5.0+ - -#### 下载 - -* [AppCenter](https://install.appcenter.ms/users/nekohasekai/apps/sfa/distribution_groups/publictest) -* [Github Releases](https://github.com/SagerNet/sing-box/releases) - -#### 注意事项 - -* 远程配置文件请求中的 User Agent 为 `SFA/$version ($version_code; sing-box $sing_box_version)` -* 工作目录位于 `/sdcard/Android/data/io.nekohasekai.sfa/files` (外部文件目录) -* 崩溃日志位于 `$working_directory/stderr.log` - -#### 隐私政策 - -* SFA 不收集或共享个人数据。 -* 软件生成的数据始终在您的设备上。 diff --git a/docs/installation/clients/sfi.md b/docs/installation/clients/sfi.md deleted file mode 100644 index 35c15c918b..0000000000 --- a/docs/installation/clients/sfi.md +++ /dev/null @@ -1,23 +0,0 @@ -# SFI - -Experimental iOS client for sing-box. - -#### Requirements - -* iOS 15.0+ -* An Apple account outside of mainland China - -#### Download - -* [AppStore](https://apps.apple.com/us/app/sing-box/id6451272673) -* [TestFlight](https://testflight.apple.com/join/AcqO44FH) - -#### Note - -* User Agent in remote profile request is `SFI/$version ($version_code; sing-box $sing_box_version)` -* Crash logs is located in `Settings` -> `View Service Log` - -#### Privacy policy - -* SFI did not collect or share personal data. -* The data generated by the software is always on your device. diff --git a/docs/installation/clients/sfi.zh.md b/docs/installation/clients/sfi.zh.md deleted file mode 100644 index 90c3845f4f..0000000000 --- a/docs/installation/clients/sfi.zh.md +++ /dev/null @@ -1,23 +0,0 @@ -# SFI - -实验性的 iOS sing-box 客户端。 - -#### 要求 - -* iOS 15.0+ -* 一个非中国大陆地区的 Apple 账号 - -#### 下载 - -* [AppStore](https://apps.apple.com/us/app/sing-box/id6451272673) -* [TestFlight](https://testflight.apple.com/join/AcqO44FH) - -#### 注意事项 - -* 远程配置文件请求中的 User Agent 为 `SFI/$version ($version_code; sing-box $sing_box_version)` -* 崩溃日志位于 `Settings` -> `View Service Log` - -#### 隐私政策 - -* SFI 不收集或共享个人数据。 -* 软件生成的数据始终在您的设备上。 diff --git a/docs/installation/clients/sfm.md b/docs/installation/clients/sfm.md deleted file mode 100644 index 8d2237c0a6..0000000000 --- a/docs/installation/clients/sfm.md +++ /dev/null @@ -1,29 +0,0 @@ -# SFM - -Experimental macOS client for sing-box. - -#### Requirements - -* macOS 13.0+ -* An Apple account outside of mainland China (App Store Version) - -#### Download (App Store Version) - -* [AppStore](https://apps.apple.com/us/app/sing-box/id6451272673) -* [TestFlight](https://testflight.apple.com/join/AcqO44FH) - -#### Download (Independent Version) - -* [GitHub Release](https://github.com/SagerNet/sing-box/releases/latest) -* Homebrew (Cask): `brew install sfm` -* Homebrew (Tap): `brew tap sagernet/sing-box && brew install sagernet/sing-box/sfm` - -#### Note - -* User Agent in remote profile request is `SFM/$version ($version_code; sing-box $sing_box_version)` -* Crash logs is located in `Settings` -> `View Service Log` - -#### Privacy policy - -* SFM did not collect or share personal data. -* The data generated by the software is always on your device. diff --git a/docs/installation/clients/sfm.zh.md b/docs/installation/clients/sfm.zh.md deleted file mode 100644 index a6e42b8186..0000000000 --- a/docs/installation/clients/sfm.zh.md +++ /dev/null @@ -1,29 +0,0 @@ -# SFM - -实验性的 macOS sing-box 客户端。 - -#### 要求 - -* macOS 13.0+ -* 一个非中国大陆地区的 Apple 账号 (商店版本) - -#### 下载 (商店版本) - -* [AppStore](https://apps.apple.com/us/app/sing-box/id6451272673) -* [TestFlight](https://testflight.apple.com/join/AcqO44FH) - -#### 下载 (独立版本) - -* [GitHub Release](https://github.com/SagerNet/sing-box/releases/latest) -* Homebrew (Cask): `brew install sfm` -* Homebrew (Tap): `brew tap sagernet/sing-box && brew install sagernet/sing-box/sfm` - -#### 注意事项 - -* 远程配置文件请求中的 User Agent 为 `SFM/$version ($version_code; sing-box $sing_box_version)` -* 崩溃日志位于 `Settings` -> `View Service Log` - -#### 隐私政策 - -* SFM 不收集或共享个人数据。 -* 软件生成的数据始终在您的设备上。 diff --git a/docs/installation/clients/sft.md b/docs/installation/clients/sft.md deleted file mode 100644 index b05bc07edf..0000000000 --- a/docs/installation/clients/sft.md +++ /dev/null @@ -1,30 +0,0 @@ -# SFT - -Experimental Apple tvOS client for sing-box. - -#### Requirements - -* tvOS 17.0+ - -#### Download - -* [AppStore](https://apps.apple.com/us/app/sing-box/id6451272673) -* [TestFlight](https://testflight.apple.com/join/AcqO44FH) - -#### Features - -Full functionality, except for: - -* Only remote configuration files can be created manually -* You need to update SFI to the latest version to import profiles from iPhone/iPad -* No iCloud profile support - -#### Note - -* User Agent in remote profile request is `SFT/$version ($version_code; sing-box $sing_box_version)` -* Crash logs is located in `Settings` -> `View Service Log` - -#### Privacy policy - -* SFT did not collect or share personal data. -* The data generated by the software is always on your device. diff --git a/docs/installation/clients/sft.zh.md b/docs/installation/clients/sft.zh.md deleted file mode 100644 index 239d76e2e1..0000000000 --- a/docs/installation/clients/sft.zh.md +++ /dev/null @@ -1,31 +0,0 @@ -# SFI - -实验性的 Apple tvOS sing-box 客户端。 - -#### 要求 - -* tvOS 17.0+ -* 一个非中国大陆地区的 Apple 账号 - -#### 下载 - -* [AppStore](https://apps.apple.com/us/app/sing-box/id6451272673) -* [TestFlight](https://testflight.apple.com/join/AcqO44FH) - -#### 特性 - -完整的功能,除了: - -* 只能手动创建远程配置文件 -* 您需要将 SFI 更新到最新版本才能从 iPhone/iPad 导入配置文件 -* 没有 iCloud 配置文件支持 - -#### 注意事项 - -* 远程配置文件请求中的 User Agent 为 `SFT/$version ($version_code; sing-box $sing_box_version)` -* 崩溃日志位于 `Settings` -> `View Service Log` - -#### 隐私政策 - -* SFT 不收集或共享个人数据。 -* 软件生成的数据始终在您的设备上。 diff --git a/docs/installation/clients/specification.md b/docs/installation/clients/specification.md deleted file mode 100644 index ec36dd78cf..0000000000 --- a/docs/installation/clients/specification.md +++ /dev/null @@ -1,25 +0,0 @@ -# Specification - -## Profile - -Profile defines a sing-box configuration with metadata in a GUI client. - -## Profile Types - -### Local - -Create a empty configuration or import from a local file. - -### iCloud (on Apple platforms) - -Create a new configuration or use an existing configuration on iCloud. - -### Remote - -Use a remote URL as the configuration source, with HTTP basic authentication and automatic update support. - -#### URL specification - -``` -sing-box://import-remote-profile?url=urlEncodedURL#urlEncodedName -``` \ No newline at end of file diff --git a/docs/installation/docker.md b/docs/installation/docker.md new file mode 100644 index 0000000000..ae1682dd70 --- /dev/null +++ b/docs/installation/docker.md @@ -0,0 +1,31 @@ +--- +icon: material/docker +--- + +# Docker + +## :material-console: Command + +```bash +docker run -d \ + -v /etc/sing-box:/etc/sing-box/ \ + --name=sing-box \ + --restart=always \ + ghcr.io/sagernet/sing-box \ + -D /var/lib/sing-box \ + -C /etc/sing-box/ run +``` + +## :material-box-shadow: Compose + +```yaml +version: "3.8" +services: + sing-box: + image: ghcr.io/sagernet/sing-box + container_name: sing-box + restart: always + volumes: + - /etc/sing-box:/etc/sing-box/ + command: -D /var/lib/sing-box -C /etc/sing-box/ run +``` diff --git a/docs/installation/from-source.md b/docs/installation/from-source.md deleted file mode 100644 index da0ffc02d8..0000000000 --- a/docs/installation/from-source.md +++ /dev/null @@ -1,50 +0,0 @@ -# Install from source - -## Requirements - -Before sing-box 1.4.0: - -* Go 1.18.5 - 1.20.x - -Since sing-box 1.4.0: - -* Go 1.18.5 - ~ -* Go 1.20.0 - ~ if `with_quic` tag enabled - -## Installation - -```bash -go install -v github.com/sagernet/sing-box/cmd/sing-box@latest -``` - -Install with options: - -```bash -go install -v -tags with_quic,with_wireguard github.com/sagernet/sing-box/cmd/sing-box@latest -``` - -| Build Tag | Description | -|------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `with_quic` | Build with QUIC support, see [QUIC and HTTP3 DNS transports](/configuration/dns/server), [Naive inbound](/configuration/inbound/naive), [Hysteria Inbound](/configuration/inbound/hysteria), [Hysteria Outbound](/configuration/outbound/hysteria) and [V2Ray Transport#QUIC](/configuration/shared/v2ray-transport#quic). | -| `with_grpc` | Build with standard gRPC support, see [V2Ray Transport#gRPC](/configuration/shared/v2ray-transport#grpc). | -| `with_dhcp` | Build with DHCP support, see [DHCP DNS transport](/configuration/dns/server). | -| `with_wireguard` | Build with WireGuard support, see [WireGuard outbound](/configuration/outbound/wireguard). | -| `with_shadowsocksr` | Build with ShadowsocksR support, see [ShadowsocksR outbound](/configuration/outbound/shadowsocksr). | -| `with_ech` | Build with TLS ECH extension support for TLS outbound, see [TLS](/configuration/shared/tls#ech). | -| `with_utls` | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](/configuration/shared/tls#utls). | -| `with_reality_server` | Build with reality TLS server support, see [TLS](/configuration/shared/tls). | -| `with_acme` | Build with ACME TLS certificate issuer support, see [TLS](/configuration/shared/tls). | -| `with_clash_api` | Build with Clash API support, see [Experimental](/configuration/experimental#clash-api-fields). | -| `with_v2ray_api` | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). | -| `with_gvisor` | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). | -| `with_embedded_tor` (CGO required) | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor). | -| `with_lwip` (CGO required) | Build with LWIP Tun stack support, see [Tun inbound](/configuration/inbound/tun#stack). | - -The binary is built under $GOPATH/bin - -```bash -sing-box version -``` - -It is also recommended to use systemd to manage sing-box service, -see [Linux server installation example](/examples/linux-server-installation). \ No newline at end of file diff --git a/docs/installation/from-source.zh.md b/docs/installation/from-source.zh.md deleted file mode 100644 index fbcb9ba827..0000000000 --- a/docs/installation/from-source.zh.md +++ /dev/null @@ -1,39 +0,0 @@ -# 从源代码安装 - -sing-box 需要 Golang **1.18.5** 或更高版本。 - -```bash -go install -v github.com/sagernet/sing-box/cmd/sing-box@latest -``` - -自定义安装: - -```bash -go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@latest -``` - -| 构建标志 | 描述 | -|------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `with_quic` | 启用 QUIC 支持,参阅 [QUIC 和 HTTP3 DNS 传输层](/configuration/dns/server),[Naive 入站](/configuration/inbound/naive),[Hysteria 入站](/configuration/inbound/hysteria),[Hysteria 出站](/configuration/outbound/hysteria) 和 [V2Ray 传输层#QUIC](/configuration/shared/v2ray-transport#quic)。 | -| `with_grpc` | 启用标准 gRPC 支持,参阅 [V2Ray 传输层#gRPC](/configuration/shared/v2ray-transport#grpc)。 | -| `with_dhcp` | 启用 DHCP 支持,参阅 [DHCP DNS 传输层](/configuration/dns/server)。 | -| `with_wireguard` | 启用 WireGuard 支持,参阅 [WireGuard 出站](/configuration/outbound/wireguard)。 | -| `with_shadowsocksr` | 启用 ShadowsocksR 支持,参阅 [ShadowsocksR 出站](/configuration/outbound/shadowsocksr)。 | -| `with_ech` | 启用 TLS ECH 扩展支持,参阅 [TLS](/configuration/shared/tls#ech)。 | -| `with_utls` | 启用 [uTLS](https://github.com/refraction-networking/utls) 支持,参阅 [TLS](/configuration/shared/tls#utls)。 | -| `with_reality_server` | 启用 reality TLS 服务器支持,参阅 [TLS](/configuration/shared/tls)。 | -| `with_acme` | 启用 ACME TLS 证书签发支持,参阅 [TLS](/configuration/shared/tls)。 | -| `with_clash_api` | 启用 Clash API 支持,参阅 [实验性](/configuration/experimental#clash-api-fields)。 | -| `with_v2ray_api` | 启用 V2Ray API 支持,参阅 [实验性](/configuration/experimental#v2ray-api-fields)。 | -| `with_gvisor` | 启用 gVisor 支持,参阅 [Tun 入站](/configuration/inbound/tun#stack) 和 [WireGuard 出站](/configuration/outbound/wireguard#system_interface)。 | -| `with_embedded_tor` (需要 CGO) | 启用 嵌入式 Tor 支持,参阅 [Tor 出站](/configuration/outbound/tor)。 | -| `with_lwip` (需要 CGO) | 启用 LWIP Tun 栈支持,参阅 [Tun 入站](/configuration/inbound/tun#stack)。 | - -二进制文件将被构建在 `$GOPATH/bin` 下。 - -```bash -sing-box version -``` - -同时推荐使用 systemd 来管理 sing-box 服务器实例。 -参阅 [Linux 服务器安装示例](/examples/linux-server-installation)。 \ No newline at end of file diff --git a/docs/installation/package-manager.md b/docs/installation/package-manager.md new file mode 100644 index 0000000000..a1721069e4 --- /dev/null +++ b/docs/installation/package-manager.md @@ -0,0 +1,66 @@ +--- +icon: material/package +--- + +# Package Manager + +## :material-download-box: Manual Installation + +=== ":material-debian: Debian / DEB" + + ```bash + bash <(curl -fsSL https://sing-box.app/deb-install.sh) + ``` + +=== ":material-redhat: Redhat / RPM" + + ```bash + bash <(curl -fsSL https://sing-box.app/rpm-install.sh) + ``` + +=== ":simple-archlinux: Archlinux / PKG" + + ```bash + bash <(curl -fsSL https://sing-box.app/arch-install.sh) + ``` + +## :material-book-lock-open: Managed Installation + +=== ":material-linux: Linux" + + | Type | Platform | Link | Command | Actively maintained | + |----------|--------------------|---------------------|------------------------------|---------------------| + | AUR | (Linux) Arch Linux | [sing-box][aur] ᴬᵁᴿ | `? -S sing-box` | :material-check: | + | nixpkgs | (Linux) NixOS | [sing-box][nixpkgs] | `nix-env -iA nixos.sing-box` | :material-check: | + | Homebrew | macOS / Linux | [sing-box][brew] | `brew install sing-box` | :material-check: | + | Alpine | (Linux) Alpine | [sing-box][alpine] | `apk add sing-box` | :material-alert: | + +=== ":material-apple: macOS" + + | Type | Platform | Link | Command | Actively maintained | + |----------|---------------|------------------|-------------------------|---------------------| + | Homebrew | macOS / Linux | [sing-box][brew] | `brew install sing-box` | :material-check: | + +=== ":material-microsoft-windows: Windows" + + | Type | Platform | Link | Command | Actively maintained | + |------------|--------------------|---------------------|------------------------------|---------------------| + | Scoop | Windows | [sing-box][scoop] | `scoop install sing-box` | :material-check: | + | Chocolatey | Windows | [sing-box][choco] | `choco install sing-box` | :material-check: | + | winget | Windows | [sing-box][winget] | `winget install sing-box` | :material-alert: | + +=== ":material-android: Android" + + | Type | Platform | Link | Command | Actively maintained | + |------------|--------------------|---------------------|------------------------------|---------------------| + | Termux | Android | [sing-box][termux] | `pkg add sing-box` | :material-check: | + + +[alpine]: https://pkgs.alpinelinux.org/packages?name=sing-box +[aur]: https://aur.archlinux.org/packages/sing-box +[nixpkgs]: https://github.com/NixOS/nixpkgs/blob/nixos-unstable/pkgs/tools/networking/sing-box/default.nix +[termux]: https://github.com/termux/termux-packages/tree/master/packages/sing-box +[brew]: https://formulae.brew.sh/formula/sing-box +[choco]: https://chocolatey.org/packages/sing-box +[scoop]: https://github.com/ScoopInstaller/Main/blob/master/bucket/sing-box.json +[winget]: https://github.com/microsoft/winget-pkgs/tree/master/manifests/s/SagerNet/sing-box \ No newline at end of file diff --git a/docs/installation/package-manager/android.md b/docs/installation/package-manager/android.md deleted file mode 100644 index 1177b86b3a..0000000000 --- a/docs/installation/package-manager/android.md +++ /dev/null @@ -1,7 +0,0 @@ -# Android - -## Termux - -```shell -pkg add sing-box -``` diff --git a/docs/installation/package-manager/macOS.md b/docs/installation/package-manager/macOS.md deleted file mode 100644 index f0192b5a6c..0000000000 --- a/docs/installation/package-manager/macOS.md +++ /dev/null @@ -1,14 +0,0 @@ -# macOS - -## Homebrew (core) - -```shell -brew install sing-box -``` - -## Homebrew (Tap) - -```shell -brew tap sagernet/sing-box -brew install sagernet/sing-box/sing-box -``` \ No newline at end of file diff --git a/docs/installation/package-manager/windows.md b/docs/installation/package-manager/windows.md deleted file mode 100644 index 46e078b93f..0000000000 --- a/docs/installation/package-manager/windows.md +++ /dev/null @@ -1,13 +0,0 @@ -# Windows - -## Chocolatey - -```shell -choco install sing-box -``` - -## winget - -```shell -winget install sing-box -``` \ No newline at end of file diff --git a/docs/installation/scripts/arch-install.sh b/docs/installation/scripts/arch-install.sh new file mode 100644 index 0000000000..50ce57679d --- /dev/null +++ b/docs/installation/scripts/arch-install.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -e -o pipefail + +ARCH_RAW=$(uname -m) +case "${ARCH_RAW}" in + 'x86_64') ARCH='amd64';; + 'x86' | 'i686' | 'i386') ARCH='386';; + 'aarch64' | 'arm64') ARCH='arm64';; + 'armv7l') ARCH='armv7';; + 's390x') ARCH='s390x';; + *) echo "Unsupported architecture: ${ARCH_RAW}"; exit 1;; +esac + +VERSION=$(curl -s https://api.github.com/repos/SagerNet/sing-box/releases/latest \ + | grep tag_name \ + | cut -d ":" -f2 \ + | sed 's/\"//g;s/\,//g;s/\ //g;s/v//') + +curl -Lo sing-box.pkg.tar.zst "https://github.com/SagerNet/sing-box/releases/download/v${VERSION}/sing-box_${VERSION}_linux_${ARCH}.pkg.tar.zst" +sudo pacman -U sing-box.pkg.tar.zst +rm sing-box.pkg.tar.zst diff --git a/docs/installation/scripts/deb-install.sh b/docs/installation/scripts/deb-install.sh new file mode 100644 index 0000000000..587b6f61ba --- /dev/null +++ b/docs/installation/scripts/deb-install.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +set -e -o pipefail + +ARCH_RAW=$(uname -m) +case "${ARCH_RAW}" in + 'x86_64') ARCH='amd64';; + 'x86' | 'i686' | 'i386') ARCH='386';; + 'aarch64' | 'arm64') ARCH='arm64';; + 'armv7l') ARCH='armv7';; + 's390x') ARCH='s390x';; + *) echo "Unsupported architecture: ${ARCH_RAW}"; exit 1;; +esac + +VERSION=$(curl -s https://api.github.com/repos/SagerNet/sing-box/releases/latest \ + | grep tag_name \ + | cut -d ":" -f2 \ + | sed 's/\"//g;s/\,//g;s/\ //g;s/v//') + +curl -Lo sing-box.deb "https://github.com/SagerNet/sing-box/releases/download/v${VERSION}/sing-box_${VERSION}_linux_${ARCH}.deb" +sudo dpkg -i sing-box.deb +rm sing-box.deb + diff --git a/docs/installation/scripts/rpm-install.sh b/docs/installation/scripts/rpm-install.sh new file mode 100644 index 0000000000..fc3d731400 --- /dev/null +++ b/docs/installation/scripts/rpm-install.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -e -o pipefail + +ARCH_RAW=$(uname -m) +case "${ARCH_RAW}" in + 'x86_64') ARCH='amd64';; + 'x86' | 'i686' | 'i386') ARCH='386';; + 'aarch64' | 'arm64') ARCH='arm64';; + 'armv7l') ARCH='armv7';; + 's390x') ARCH='s390x';; + *) echo "Unsupported architecture: ${ARCH_RAW}"; exit 1;; +esac + +VERSION=$(curl -s https://api.github.com/repos/SagerNet/sing-box/releases/latest \ + | grep tag_name \ + | cut -d ":" -f2 \ + | sed 's/\"//g;s/\,//g;s/\ //g;s/v//') + +curl -Lo sing-box.rpm "https://github.com/SagerNet/sing-box/releases/download/v${VERSION}/sing-box_${VERSION}_linux_${ARCH}.rpm" +sudo rpm -i sing-box.rpm +rm sing-box.rpm diff --git a/docs/manual/proxy-protocol/hysteria2.md b/docs/manual/proxy-protocol/hysteria2.md new file mode 100644 index 0000000000..9cc07e6f12 --- /dev/null +++ b/docs/manual/proxy-protocol/hysteria2.md @@ -0,0 +1,211 @@ +--- +icon: material/lightning-bolt +--- + +# Hysteria 2 + +The most popular Chinese-made simple protocol based on QUIC, the selling point is Brutal, +a congestion control algorithm that can resist packet loss by manually specifying the required rate by the user. + +!!! warning + + Even though GFW rarely blocks UDP-based proxies, such protocols actually have far more characteristics than TCP based proxies. + +| Specification | Binary Characteristics | Active Detect Hiddenness | +|---------------------------------------------------------------------------|------------------------|--------------------------| +| [hysteria.network](https://v2.hysteria.network/docs/developers/Protocol/) | :material-alert: | :material-check: | + +## :material-text-box-check: Password Generator + +| Generate Password | Action | +|----------------------------|-----------------------------------------------------------------| +| | | + + + +## :material-alert: Difference from official Hysteria + +The official program supports an authentication method called **userpass**, +which essentially uses a combination of `:` as the actual password, +while sing-box does not provide this alias. +To use sing-box with the official program, you need to fill in that combination as the actual password. + +## :material-server: Server Example + +!!! info "" + + Replace `up_mbps` and `down_mbps` values with the actual bandwidth of your server. + +=== ":material-harddisk: With local certificate" + + ```json + { + "inbounds": [ + { + "type": "hysteria2", + "listen": "::", + "listen_port": 8080, + "up_mbps": 100, + "down_mbps": 100, + "users": [ + { + "name": "sekai", + "password": "" + } + ], + "tls": { + "enabled": true, + "server_name": "example.org", + "key_path": "/path/to/key.pem", + "certificate_path": "/path/to/certificate.pem" + } + } + ] + } + ``` + +=== ":material-auto-fix: With ACME" + + ```json + { + "inbounds": [ + { + "type": "hysteria2", + "listen": "::", + "listen_port": 8080, + "up_mbps": 100, + "down_mbps": 100, + "users": [ + { + "name": "sekai", + "password": "" + } + ], + "tls": { + "enabled": true, + "server_name": "example.org", + "acme": { + "domain": "example.org", + "email": "admin@example.org" + } + } + } + ] + } + ``` + +=== ":material-cloud: With ACME and Cloudflare API" + + ```json + { + "inbounds": [ + { + "type": "hysteria2", + "listen": "::", + "listen_port": 8080, + "up_mbps": 100, + "down_mbps": 100, + "users": [ + { + "name": "sekai", + "password": "" + } + ], + "tls": { + "enabled": true, + "server_name": "example.org", + "acme": { + "domain": "example.org", + "email": "admin@example.org", + "dns01_challenge": { + "provider": "cloudflare", + "api_token": "my_token" + } + } + } + } + ] + } + ``` + +## :material-cellphone-link: Client Example + +!!! info "" + + Replace `up_mbps` and `down_mbps` values with the actual bandwidth of your client. + +=== ":material-web-check: With valid certificate" + + ```json + { + "outbounds": [ + { + "type": "hysteria2", + "server": "127.0.0.1", + "server_port": 8080, + "up_mbps": 100, + "down_mbps": 100, + "password": "", + "tls": { + "enabled": true, + "server_name": "example.org" + } + } + ] + } + ``` + +=== ":material-check: With self-sign certificate" + + !!! info "Tip" + + Use `sing-box merge` command to merge configuration and certificate into one file. + + ```json + { + "outbounds": [ + { + "type": "hysteria2", + "server": "127.0.0.1", + "server_port": 8080, + "up_mbps": 100, + "down_mbps": 100, + "password": "", + "tls": { + "enabled": true, + "server_name": "example.org", + "certificate_path": "/path/to/certificate.pem" + } + } + ] + } + ``` + +=== ":material-alert: Ignore certificate verification" + + ```json + { + "outbounds": [ + { + "type": "hysteria2", + "server": "127.0.0.1", + "server_port": 8080, + "up_mbps": 100, + "down_mbps": 100, + "password": "", + "tls": { + "enabled": true, + "server_name": "example.org", + "insecure": true + } + } + ] + } + ``` diff --git a/docs/manual/proxy-protocol/shadowsocks.md b/docs/manual/proxy-protocol/shadowsocks.md new file mode 100644 index 0000000000..be0bc20800 --- /dev/null +++ b/docs/manual/proxy-protocol/shadowsocks.md @@ -0,0 +1,126 @@ +--- +icon: material/send +--- + +# Shadowsocks + +As the most well-known Chinese-made proxy protocol, +Shadowsocks exists in multiple versions, +but only AEAD 2022 ciphers TCP with multiplexing is recommended. + +| Ciphers | Specification | Cryptographic Security | Binary Characteristics | Active Detect Hiddenness | +|----------------|------------------------------------------------------------|------------------------|------------------------|--------------------------| +| Stream Ciphers | [shadowsocks.org](https://shadowsocks.org/doc/stream.html) | :material-alert: | :material-alert: | :material-alert: | +| AEAD | [shadowsocks.org](https://shadowsocks.org/doc/aead.html) | :material-check: | :material-alert: | :material-alert: | +| AEAD 2022 | [shadowsocks.org](https://shadowsocks.org/doc/sip022.html) | :material-check: | :material-check: | :material-help: | + +## :material-text-box-check: Password Generator + +| For `2022-blake3-aes-128-gcm` cipher | For other ciphers | Action | +|--------------------------------------|-------------------------------|-----------------------------------------------------------------| +| | | | + + + +## :material-server: Server Example + +!!! info "" + + Password of cipher `2022-blake3-aes-128-gcm` can be generated by command `sing-box generate rand 16 --base64` + +=== ":material-account: Single-user" + + ```json + { + "inbounds": [ + { + "type": "shadowsocks", + "listen": "::", + "listen_port": 8080, + "network": "tcp", + "method": "2022-blake3-aes-128-gcm", + "password": "", + "multiplex": { + "enabled": true + } + } + ] + } + ``` + +=== ":material-account-multiple: Multi-user" + + ```json + { + "inbounds": [ + { + "type": "shadowsocks", + "listen": "::", + "listen_port": 8080, + "network": "tcp", + "method": "2022-blake3-aes-128-gcm", + "password": "", + "users": [ + { + "name": "sekai", + "password": "" + } + ], + "multiplex": { + "enabled": true + } + } + ] + } + ``` + +## :material-cellphone-link: Client Example + +=== ":material-account: Single-user" + + ```json + { + "outbounds": [ + { + "type": "shadowsocks", + "server": "127.0.0.1", + "server_port": 8080, + "method": "2022-blake3-aes-128-gcm", + "password": "", + "multiplex": { + "enabled": true + } + } + ] + } + ``` + +=== ":material-account-multiple: Multi-user" + + ```json + { + "outbounds": [ + { + "type": "shadowsocks", + "server": "127.0.0.1", + "server_port": 8080, + "method": "2022-blake3-aes-128-gcm", + "password": ":", + "multiplex": { + "enabled": true + } + } + ] + } + ``` diff --git a/docs/manual/proxy-protocol/trojan.md b/docs/manual/proxy-protocol/trojan.md new file mode 100644 index 0000000000..4c2e16f17e --- /dev/null +++ b/docs/manual/proxy-protocol/trojan.md @@ -0,0 +1,214 @@ +--- +icon: material/horse +--- + +# Trojan + +As the most commonly used TLS proxy made in China, Trojan can be used in various combinations, +but only the combination of uTLS and multiplexing is recommended. + +| Protocol and implementation combination | Specification | Binary Characteristics | Active Detect Hiddenness | +|-----------------------------------------|----------------------------------------------------------------------|------------------------|--------------------------| +| Origin / trojan-gfw | [trojan-gfw.github.io](https://trojan-gfw.github.io/trojan/protocol) | :material-check: | :material-check: | +| Basic Go implementation | / | :material-alert: | :material-check: | +| with privates transport by V2Ray | No formal definition | :material-alert: | :material-alert: | +| with uTLS enabled | No formal definition | :material-help: | :material-check: | + +## :material-text-box-check: Password Generator + +| Generate Password | Action | +|----------------------------|-----------------------------------------------------------------| +| | | + + + +## :material-server: Server Example + +=== ":material-harddisk: With local certificate" + + ```json + { + "inbounds": [ + { + "type": "trojan", + "listen": "::", + "listen_port": 8080, + "users": [ + { + "name": "example", + "password": "password" + } + ], + "tls": { + "enabled": true, + "server_name": "example.org", + "key_path": "/path/to/key.pem", + "certificate_path": "/path/to/certificate.pem" + }, + "multiplex": { + "enabled": true + } + } + ] + } + ``` + +=== ":material-auto-fix: With ACME" + + ```json + { + "inbounds": [ + { + "type": "trojan", + "listen": "::", + "listen_port": 8080, + "users": [ + { + "name": "example", + "password": "password" + } + ], + "tls": { + "enabled": true, + "server_name": "example.org", + "acme": { + "domain": "example.org", + "email": "admin@example.org" + } + }, + "multiplex": { + "enabled": true + } + } + ] + } + ``` + +=== ":material-cloud: With ACME and Cloudflare API" + + ```json + { + "inbounds": [ + { + "type": "trojan", + "listen": "::", + "listen_port": 8080, + "users": [ + { + "name": "example", + "password": "password" + } + ], + "tls": { + "enabled": true, + "server_name": "example.org", + "acme": { + "domain": "example.org", + "email": "admin@example.org", + "dns01_challenge": { + "provider": "cloudflare", + "api_token": "my_token" + } + } + }, + "multiplex": { + "enabled": true + } + } + ] + } + ``` + +## :material-cellphone-link: Client Example + +=== ":material-web-check: With valid certificate" + + ```json + { + "outbounds": [ + { + "type": "trojan", + "server": "127.0.0.1", + "server_port": 8080, + "password": "password", + "tls": { + "enabled": true, + "server_name": "example.org", + "utls": { + "enabled": true, + "fingerprint": "firefox" + } + }, + "multiplex": { + "enabled": true + } + } + ] + } + ``` + +=== ":material-check: With self-sign certificate" + + !!! info "Tip" + + Use `sing-box merge` command to merge configuration and certificate into one file. + + ```json + { + "outbounds": [ + { + "type": "trojan", + "server": "127.0.0.1", + "server_port": 8080, + "password": "password", + "tls": { + "enabled": true, + "server_name": "example.org", + "certificate_path": "/path/to/certificate.pem", + "utls": { + "enabled": true, + "fingerprint": "firefox" + } + }, + "multiplex": { + "enabled": true + } + } + ] + } + ``` + +=== ":material-alert: Ignore certificate verification" + + ```json + { + "outbounds": [ + { + "type": "trojan", + "server": "127.0.0.1", + "server_port": 8080, + "password": "password", + "tls": { + "enabled": true, + "server_name": "example.org", + "insecure": true, + "utls": { + "enabled": true, + "fingerprint": "firefox" + } + }, + "multiplex": { + "enabled": true + } + } + ] + } + ``` + diff --git a/docs/manual/proxy-protocol/tuic.md b/docs/manual/proxy-protocol/tuic.md new file mode 100644 index 0000000000..632be317c8 --- /dev/null +++ b/docs/manual/proxy-protocol/tuic.md @@ -0,0 +1,208 @@ +--- +icon: material/alpha-t-box +--- + +# TUIC + +A recently popular Chinese-made simple protocol based on QUIC, the selling point is the BBR congestion control algorithm. + +!!! warning + + Even though GFW rarely blocks UDP-based proxies, such protocols actually have far more characteristics than TCP based proxies. + +| Specification | Binary Characteristics | Active Detect Hiddenness | +|-----------------------------------------------------------|------------------------|--------------------------| +| [Github](https://github.com/EAimTY/tuic/blob/dev/SPEC.md) | :material-alert: | :material-check: | + +## Password Generator + +| Generated UUID | Generated Password | Action | +|------------------------|----------------------------|-----------------------------------------------------------------| +| | | | + + + +## :material-server: Server Example + +=== ":material-harddisk: With local certificate" + + ```json + { + "inbounds": [ + { + "type": "tuic", + "listen": "::", + "listen_port": 8080, + "users": [ + { + "name": "sekai", + "uuid": "", + "password": "" + } + ], + "congestion_control": "bbr", + "tls": { + "enabled": true, + "server_name": "example.org", + "key_path": "/path/to/key.pem", + "certificate_path": "/path/to/certificate.pem" + } + } + ] + } + ``` + +=== ":material-auto-fix: With ACME" + + ```json + { + "inbounds": [ + { + "type": "tuic", + "listen": "::", + "listen_port": 8080, + "users": [ + { + "name": "sekai", + "uuid": "", + "password": "" + } + ], + "congestion_control": "bbr", + "tls": { + "enabled": true, + "server_name": "example.org", + "acme": { + "domain": "example.org", + "email": "admin@example.org" + } + } + } + ] + } + ``` + +=== ":material-cloud: With ACME and Cloudflare API" + + ```json + { + "inbounds": [ + { + "type": "tuic", + "listen": "::", + "listen_port": 8080, + "users": [ + { + "name": "sekai", + "uuid": "", + "password": "" + } + ], + "congestion_control": "bbr", + "tls": { + "enabled": true, + "server_name": "example.org", + "acme": { + "domain": "example.org", + "email": "admin@example.org", + "dns01_challenge": { + "provider": "cloudflare", + "api_token": "my_token" + } + } + } + } + ] + } + ``` + +## :material-cellphone-link: Client Example + +=== ":material-web-check: With valid certificate" + + ```json + { + "outbounds": [ + { + "type": "tuic", + "server": "127.0.0.1", + "server_port": 8080, + "uuid": "", + "password": "", + "congestion_control": "bbr", + "tls": { + "enabled": true, + "server_name": "example.org" + } + } + ] + } + ``` + +=== ":material-check: With self-sign certificate" + + !!! info "Tip" + + Use `sing-box merge` command to merge configuration and certificate into one file. + + ```json + { + "outbounds": [ + { + "type": "tuic", + "server": "127.0.0.1", + "server_port": 8080, + "uuid": "", + "password": "", + "congestion_control": "bbr", + "tls": { + "enabled": true, + "server_name": "example.org", + "certificate_path": "/path/to/certificate.pem" + } + } + ] + } + ``` + +=== ":material-alert: Ignore certificate verification" + + ```json + { + "outbounds": [ + { + "type": "tuic", + "server": "127.0.0.1", + "server_port": 8080, + "uuid": "", + "password": "", + "congestion_control": "bbr", + "tls": { + "enabled": true, + "server_name": "example.org", + "insecure": true + } + } + ] + } + ``` + diff --git a/docs/manual/proxy/client.md b/docs/manual/proxy/client.md new file mode 100644 index 0000000000..675c2af095 --- /dev/null +++ b/docs/manual/proxy/client.md @@ -0,0 +1,287 @@ +--- +icon: material/cellphone-link +--- + +# Client + +### :material-ray-start: Introduction + +For a long time, the modern usage and principles of proxy clients +for graphical operating systems have not been clearly described. +However, we can categorize them into three types: +system proxy, firewall redirection, and virtual interface. + +### :material-web-refresh: System Proxy + +Almost all graphical environments support system-level proxies, +which are essentially ordinary HTTP proxies that only support TCP. + +| Operating System / Desktop Environment | System Proxy | Application Support | +|:---------------------------------------------|:-------------------------------------|:--------------------| +| Windows | :material-check: | :material-check: | +| macOS | :material-check: | :material-check: | +| GNOME/KDE | :material-check: | :material-check: | +| Android | ROOT or adb (permission) is required | :material-check: | +| Android/iOS (with sing-box graphical client) | via `tun.platform.http_proxy` | :material-check: | + +As one of the most well-known proxy methods, it has many shortcomings: +many TCP clients that are not based on HTTP do not check and use the system proxy. +Moreover, UDP and ICMP traffics bypass the proxy. + +```mermaid +flowchart LR + dns[DNS query] -- Is HTTP request? --> proxy[HTTP proxy] + dns --> leak[Leak] + tcp[TCP connection] -- Is HTTP request? --> proxy + tcp -- Check and use HTTP CONNECT? --> proxy + tcp --> leak + udp[UDP packet] --> leak +``` + +### :material-wall-fire: Firewall Redirection + +This type of usage typically relies on the firewall or hook interface provided by the operating system, +such as Windows’ WFP, Linux’s redirect, TProxy and eBPF, and macOS’s pf. +Although it is intrusive and cumbersome to configure, +it remains popular within the community of amateur proxy open source projects like V2Ray, +due to the low technical requirements it imposes on the software. + +### :material-expansion-card: Virtual Interface + +All L2/L3 proxies (seriously defined VPNs, such as OpenVPN, WireGuard) are based on virtual network interfaces, +which is also the only way for all L4 proxies to work as VPNs on mobile platforms like Android, iOS. + +The sing-box inherits and develops clash-premium’s TUN inbound (L3 to L4 conversion) +as the most reasonable method for performing transparent proxying. + +```mermaid +flowchart TB + packet[IP Packet] + packet --> windows[Windows / macOS] + packet --> linux[Linux] + tun[TUN interface] + windows -. route .-> tun + linux -. iproute2 route/rule .-> tun + tun --> gvisor[gVisor TUN stack] + tun --> system[system TUN stack] + assemble([L3 to L4 assemble]) + gvisor --> assemble + system --> assemble + assemble --> conn[TCP and UDP connections] + conn --> router[sing-box Router] + router --> direct[Direct outbound] + router --> proxy[Proxy outbounds] + router -- DNS hijack --> dns_out[DNS outbound] + dns_out --> dns_router[DNS router] + dns_router --> router + direct --> adi([auto detect interface]) + proxy --> adi + adi --> default[Default network interface in the system] + default --> destination[Destination server] + default --> proxy_server[Proxy server] + proxy_server --> destination +``` + +## :material-cellphone-link: Examples + +### Basic TUN usage for Chinese users + +=== ":material-numeric-4-box: IPv4 only" + + ```json + { + "dns": { + "servers": [ + { + "tag": "google", + "address": "tls://8.8.8.8" + }, + { + "tag": "local", + "address": "223.5.5.5", + "detour": "direct" + } + ], + "rules": [ + { + "outbound": "any", + "server": "local" + } + ], + "strategy": "ipv4_only" + }, + "inbounds": [ + { + "type": "tun", + "inet4_address": "172.19.0.1/30", + "auto_route": true, + "strict_route": false + } + ], + "outbounds": [ + // ... + { + "type": "direct", + "tag": "direct" + }, + { + "type": "dns", + "tag": "dns-out" + } + ], + "route": { + "rules": [ + { + "protocol": "dns", + "outbound": "dns-out" + }, + { + "geoip": [ + "private" + ], + "outbound": "direct" + } + ], + "auto_detect_interface": true + } + } + ``` + +=== ":material-numeric-6-box: IPv4 & IPv6" + + ```json + { + "dns": { + "servers": [ + { + "tag": "google", + "address": "tls://8.8.8.8" + }, + { + "tag": "local", + "address": "223.5.5.5", + "detour": "direct" + } + ], + "rules": [ + { + "outbound": "any", + "server": "local" + } + ] + }, + "inbounds": [ + { + "type": "tun", + "inet4_address": "172.19.0.1/30", + "inet6_address": "fdfe:dcba:9876::1/126", + "auto_route": true, + "strict_route": false + } + ], + "outbounds": [ + // ... + { + "type": "direct", + "tag": "direct" + }, + { + "type": "dns", + "tag": "dns-out" + } + ], + "route": { + "rules": [ + { + "protocol": "dns", + "outbound": "dns-out" + }, + { + "geoip": [ + "private" + ], + "outbound": "direct" + } + ], + "auto_detect_interface": true + } + } + ``` + +=== ":material-domain-switch: FakeIP" + + ```json + { + "dns": { + "servers": [ + { + "tag": "google", + "address": "tls://8.8.8.8" + }, + { + "tag": "local", + "address": "223.5.5.5", + "detour": "direct" + }, + { + "tag": "remote", + "address": "fakeip" + } + ], + "rules": [ + { + "outbound": "any", + "server": "local" + }, + { + "query_type": [ + "A", + "AAAA" + ], + "server": "remote" + } + ], + "fakeip": { + "enabled": true, + "inet4_range": "198.18.0.0/15", + "inet6_range": "fc00::/18" + }, + "independent_cache": true + }, + "inbounds": [ + { + "type": "tun", + "inet4_address": "172.19.0.1/30", + "inet6_address": "fdfe:dcba:9876::1/126", + "auto_route": true, + "strict_route": true + } + ], + "outbounds": [ + // ... + { + "type": "direct", + "tag": "direct" + }, + { + "type": "dns", + "tag": "dns-out" + } + ], + "route": { + "rules": [ + { + "protocol": "dns", + "outbound": "dns-out" + }, + { + "geoip": [ + "private" + ], + "outbound": "direct" + } + ], + "auto_detect_interface": true + } + } + ``` \ No newline at end of file diff --git a/docs/manual/proxy/server.md b/docs/manual/proxy/server.md new file mode 100644 index 0000000000..4d2667e039 --- /dev/null +++ b/docs/manual/proxy/server.md @@ -0,0 +1,10 @@ +--- +icon: material/server +--- + +# Server + +To use sing-box as a proxy protocol server, you pretty much only need to configure the inbound for that protocol. + +The Proxy Protocol menu below contains descriptions and configuration examples +of recommended protocols for bypassing GFW. diff --git a/docs/manual/proxy/tun.md b/docs/manual/proxy/tun.md new file mode 100644 index 0000000000..65cfb1cfd1 --- /dev/null +++ b/docs/manual/proxy/tun.md @@ -0,0 +1,66 @@ +# :material-expansion-card: TUN + +## :material-text-box: Definition + +Refers to TUNnel, a virtual network device supported by the kernel. +It’s also used in sing-box to denote the extensive functionality surrounding TUN inbound: +including traffic assembly, automatic routing, and network and default interface monitoring. + +The following flow chart describes the minimal TUN-based transparent proxy process in sing-box: + +``` mermaid +flowchart LR + subgraph inbound [Inbound] + direction TB + packet[IP Packet] + packet --> windows[Windows / macOS] + packet --> linux[Linux] + tun[TUN interface] + windows -. route .-> tun + linux -. iproute2 route/rule .-> tun + tun --> gvisor[gVisor TUN stack] + tun --> system[system TUN stack] + assemble([L3 to L4 assemble]) + gvisor --> assemble + system --> assemble + assemble --> conn[TCP and UDP connections] + conn --> router[sing-box Router] + end + + subgraph outbound [Outbound] + direction TB + direct[Direct outbound] + proxy[Proxy outbounds] + direct --> adi([auto detect interface]) + proxy --> adi + adi --> default[Default network interface in the system] + default --> destination[Destination server] + default --> proxy_server[Proxy server] + proxy_server --> destination + end + + inbound --> outbound +``` + +## :material-help-box: How to + +A basic TUN-based transparent proxy configuration file includes: an TUN inbound, `route.auto_detect_interface`, like: + +```json +{ + "inbounds": [ + { + "type": "tun", + "inet4_address": "172.19.0.1/30", + "inet6_address": "fdfe:dcba:9876::1/126", + "auto_route": true, + "strict_route": true + } + ], + "route": { + "auto_detect_interface": true + } +} +``` + +TODO: finish this wiki \ No newline at end of file diff --git a/docs/support.md b/docs/support.md index 257b343992..ec5aaecc91 100644 --- a/docs/support.md +++ b/docs/support.md @@ -1,4 +1,13 @@ -Github Issue: [Issues · SagerNet/sing-box](https://github.com/SagerNet/sing-box/issues) -Telegram Notification channel: [@yapnc](https://t.me/yapnc) -Telegram User group: [@yapug](https://t.me/yapug) -Email: [contact@sagernet.org](mailto:contact@sagernet.org) +--- +icon: material/forum +--- + +# Support + +| Channel | Link | +|:------------------------------|:--------------------------------------------| +| Community | https://community.sagernet.org | +| Github Issues | https://github.com/SagerNet/sing-box/issues | +| Telegram notification channel | https://t.me/yapnc | +| Telegram user group | https://t.me/yapug | +| Email | contact@sagernet.org | diff --git a/docs/support.zh.md b/docs/support.zh.md index 031bf6f9a4..11e04218f3 100644 --- a/docs/support.zh.md +++ b/docs/support.zh.md @@ -1,4 +1,14 @@ -Github 工单: [Issues · SagerNet/sing-box](https://github.com/SagerNet/sing-box/issues) -Telegram 通知频道: [@yapnc](https://t.me/yapnc) -Telegram 用户组: [@yapug](https://t.me/yapug) -Email: [contact@sagernet.org](mailto:contact@sagernet.org) +--- +icon: material/forum +--- + +# 支持 + +| 通道 | 链接 | +|:--------------|:--------------------------------------------| +| 社区 | https://community.sagernet.org | +| Github Issues | https://github.com/SagerNet/sing-box/issues | +| Telegram 通知频道 | https://t.me/yapnc | +| Telegram 用户组 | https://t.me/yapug | +| 邮件 | contact@sagernet.org | + diff --git a/mkdocs.yml b/mkdocs.yml index 98c46c6fb9..1d4b1d8b0e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -29,25 +29,39 @@ theme: - navigation.expand - navigation.sections - header.autohide + - content.code.copy + - content.code.select + - content.code.annotate nav: - Home: - index.md - - Features: features.md - Deprecated: deprecated.md - Support: support.md - Change Log: changelog.md - Installation: - - From source: installation/from-source.md - - Package Manager: - - macOS: installation/package-manager/macOS.md - - Windows: installation/package-manager/windows.md - - Android: installation/package-manager/android.md - - Clients: - - Specification: installation/clients/specification.md - - iOS: installation/clients/sfi.md - - macOS: installation/clients/sfm.md - - Apple tvOS: installation/clients/sft.md - - Android: installation/clients/sfa.md + - Package Manager: installation/package-manager.md + - Docker: installation/docker.md + - Build from source: installation/build-from-source.md + - Graphical Clients: + - clients/index.md + - Android: + - clients/android/index.md + - Features: clients/android/features.md + - Apple platforms: + - clients/apple/index.md + - Features: clients/apple/features.md + - General: clients/general.md + - Privacy policy: clients/privacy.md + - Manual: + - Proxy: + - Server: manual/proxy/server.md + - Client: manual/proxy/client.md +# - TUN: manual/proxy/tun.md + - Proxy Protocol: + - Shadowsocks: manual/proxy-protocol/shadowsocks.md + - Trojan: manual/proxy-protocol/trojan.md + - TUIC: manual/proxy-protocol/tuic.md + - Hysteria 2: manual/proxy-protocol/hysteria2.md - Configuration: - configuration/index.md - Log: @@ -115,24 +129,6 @@ nav: - DNS: configuration/outbound/dns.md - Selector: configuration/outbound/selector.md - URLTest: configuration/outbound/urltest.md - - FAQ: - - faq/index.md - - FakeIP: faq/fakeip.md - - Known Issues: faq/known-issues.md - - Examples: - - examples/index.md - - Linux Server Installation: examples/linux-server-installation.md - - Tun: examples/tun.md - - DNS Hijack: examples/dns-hijack.md - - Shadowsocks: examples/shadowsocks.md - - ShadowTLS: examples/shadowtls.md - - Clash API: examples/clash-api.md - - FakeIP: examples/fakeip.md - - Contributing: - - contributing/index.md - - Developing: - - Environment: contributing/environment.md - - Sub projects: contributing/sub-projects.md markdown_extensions: - pymdownx.inlinehilite - pymdownx.snippets @@ -143,6 +139,7 @@ markdown_extensions: - pymdownx.keys - pymdownx.mark - pymdownx.tilde + - pymdownx.magiclink - admonition - attr_list - md_in_html @@ -154,6 +151,14 @@ markdown_extensions: alternate_style: true - pymdownx.tasklist: custom_checkbox: true + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format extra: social: - icon: fontawesome/brands/github @@ -162,48 +167,58 @@ extra: plugins: - search - i18n: - default_language: en + docs_structure: suffix + fallback_to_default: true languages: - en: + - build: true + default: true + locale: en name: English - build: false - zh: + - build: true + default: false + locale: zh name: 简体中文 - material_alternate: true - nav_translations: - zh: - Getting Started: 开始 - Features: 特性 - Support: 支持 - Change Log: 更新日志 + nav_translations: + Home: 开始 + Deprecated: 废弃功能列表 + Support: 支持 + Change Log: 更新日志 - Installation: 安装 - From source: 从源代码 - Clients: 客户端 + Installation: 安装 + Package Manager: 包管理器 + Build from source: 从源代码构建 - Configuration: 配置 - Log: 日志 - DNS Server: DNS 服务器 - DNS Rule: DNS 规则 + Graphical Clients: 图形界面客户端 + Features: 特性 + Apple platforms: Apple 平台 + General: 通用 + Privacy policy: 隐私政策 - Route: 路由 - Route Rule: 路由规则 - Protocol Sniff: 协议探测 + Configuration: 配置 + Log: 日志 + DNS Server: DNS 服务器 + DNS Rule: DNS 规则 - Experimental: 实验性 + Route: 路由 + Route Rule: 路由规则 + Protocol Sniff: 协议探测 - Shared: 通用 - Listen Fields: 监听字段 - Dial Fields: 拨号字段 - DNS01 Challenge Fields: DNS01 验证字段 - Multiplex: 多路复用 - V2Ray Transport: V2Ray 传输层 + Experimental: 实验性 - Inbound: 入站 - Outbound: 出站 + Shared: 通用 + Listen Fields: 监听字段 + Dial Fields: 拨号字段 + DNS01 Challenge Fields: DNS01 验证字段 + Multiplex: 多路复用 + V2Ray Transport: V2Ray 传输层 - FAQ: 常见问题 - Known Issues: 已知问题 - Examples: 示例 - Linux Server Installation: Linux 服务器安装 - DNS Hijack: DNS 劫持 + Inbound: 入站 + Outbound: 出站 + + FAQ: 常见问题 + Known Issues: 已知问题 + Examples: 示例 + Linux Server Installation: Linux 服务器安装 + DNS Hijack: DNS 劫持 + reconfigure_material: true + reconfigure_search: true \ No newline at end of file From 8bd15d028275f54c65ece4f9a091cff0af17b3e4 Mon Sep 17 00:00:00 2001 From: wyx2685 Date: Sun, 19 Nov 2023 19:06:39 +0800 Subject: [PATCH 11/11] Fix udp flow mismatch error. Signed-off-by: wyx2685 --- transport/vless/service.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/vless/service.go b/transport/vless/service.go index 7b69020279..ae329ba4fa 100644 --- a/transport/vless/service.go +++ b/transport/vless/service.go @@ -70,7 +70,7 @@ func (s *Service[T]) NewConnection(ctx context.Context, conn net.Conn, metadata userFlow := s.userFlow[user] if request.Flow == FlowVision && request.Command == vmess.NetworkUDP { return E.New(FlowVision, " flow does not support UDP") - } else if request.Flow != userFlow { + } else if request.Flow != userFlow && request.Command != vmess.CommandUDP { return E.New("flow mismatch: expected ", flowName(userFlow), ", but got ", flowName(request.Flow)) }