Compare commits

..

3 Commits

Author SHA1 Message Date
M1Screw
05b4d98852 feat: sentry debug 2023-11-05 17:15:15 +08:00
世界
e82dab027d documentation: Bump version 2023-10-30 13:59:49 +08:00
世界
9350f3983b docs: Remove obsolete fields 2023-10-30 13:59:49 +08:00
25 changed files with 303 additions and 331 deletions

View File

@@ -1,6 +1,7 @@
package main package main
import ( import (
"github.com/getsentry/sentry-go"
"os" "os"
"time" "time"
@@ -15,6 +16,7 @@ var (
configDirectories []string configDirectories []string
workingDir string workingDir string
disableColor bool disableColor bool
enableDebug bool
) )
var mainCommand = &cobra.Command{ var mainCommand = &cobra.Command{
@@ -27,12 +29,25 @@ func init() {
mainCommand.PersistentFlags().StringArrayVarP(&configDirectories, "config-directory", "C", nil, "set configuration directory path") mainCommand.PersistentFlags().StringArrayVarP(&configDirectories, "config-directory", "C", nil, "set configuration directory path")
mainCommand.PersistentFlags().StringVarP(&workingDir, "directory", "D", "", "set working directory") mainCommand.PersistentFlags().StringVarP(&workingDir, "directory", "D", "", "set working directory")
mainCommand.PersistentFlags().BoolVarP(&disableColor, "disable-color", "", false, "disable color output") mainCommand.PersistentFlags().BoolVarP(&disableColor, "disable-color", "", false, "disable color output")
mainCommand.PersistentFlags().BoolVarP(&enableDebug, "debug", "", false, "enable sentry debug mode")
} }
func main() { func main() {
if err := mainCommand.Execute(); err != nil { if err := mainCommand.Execute(); err != nil {
log.Fatal(err) log.Fatal(err)
} }
if enableDebug {
err := sentry.Init(sentry.ClientOptions{
Dsn: "",
})
if err != nil {
log.Fatal("sentry.Init: %s", err)
}
defer sentry.Flush(2 * time.Second)
}
} }
func preRun(cmd *cobra.Command, args []string) { func preRun(cmd *cobra.Command, args []string) {

View File

@@ -6,7 +6,6 @@ import (
"sync" "sync"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing/common/bufio/deadline"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
@@ -45,14 +44,7 @@ func (d *DetourDialer) DialContext(ctx context.Context, network string, destinat
if err != nil { if err != nil {
return nil, err return nil, err
} }
conn, err := dialer.DialContext(ctx, network, destination) return 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) { func (d *DetourDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {

View File

@@ -1,20 +1,27 @@
#### 1.7.0-alpha.2 #### 1.6.0
* 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 * Fixes and improvements
Important changes since 1.5:
* Our [Apple tvOS client](/installation/clients/sft) is now available in the App Store 🍎
* Update BBR congestion control for TUIC and Hysteria2 **1**
* Update brutal congestion control for Hysteria2
* Add `brutal_debug` option for Hysteria2
* Update legacy Hysteria protocol **2**
* Add TLS self sign key pair generate command
* Remove [Deprecated Features](/deprecated) by agreement
**1**: **1**:
If enabled, for UDP proxy requests addressed to a domain, None of the existing Golang BBR congestion control implementations have been reviewed or unit tested.
the original packet address will be sent in the response instead of the mapped domain. This update is intended to address the multi-send defects of the old implementation and may introduce new issues.
**2**
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
This option is used for compatibility with clients that
do not support receiving UDP packets with domain addresses, such as Surge.
#### 1.5.5 #### 1.5.5

View File

@@ -22,12 +22,6 @@
"::/1", "::/1",
"8000::/1" "8000::/1"
], ],
"inet4_route_exclude_address": [
"192.168.0.0/16"
],
"inet6_route_exclude_address": [
"fc00::/7"
],
"endpoint_independent_nat": false, "endpoint_independent_nat": false,
"stack": "system", "stack": "system",
"include_interface": [ "include_interface": [
@@ -136,14 +130,6 @@ Use custom routes instead of default when `auto_route` is enabled.
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 #### endpoint_independent_nat
!!! info "" !!! info ""

View File

@@ -22,12 +22,6 @@
"::/1", "::/1",
"8000::/1" "8000::/1"
], ],
"inet4_route_exclude_address": [
"192.168.0.0/16"
],
"inet6_route_exclude_address": [
"fc00::/7"
],
"endpoint_independent_nat": false, "endpoint_independent_nat": false,
"stack": "system", "stack": "system",
"include_interface": [ "include_interface": [
@@ -137,14 +131,6 @@ tun 接口的 IPv6 前缀。
启用 `auto_route` 时使用自定义路由而不是默认路由。 启用 `auto_route` 时使用自定义路由而不是默认路由。
#### inet4_route_exclude_address
启用 `auto_route` 时排除自定义路由。
#### inet6_route_exclude_address
启用 `auto_route` 时排除自定义路由。
#### endpoint_independent_nat #### endpoint_independent_nat
启用独立于端点的 NAT。 启用独立于端点的 NAT。

View File

@@ -7,26 +7,28 @@
"tcp_fast_open": false, "tcp_fast_open": false,
"tcp_multi_path": false, "tcp_multi_path": false,
"udp_fragment": false, "udp_fragment": false,
"udp_timeout": 300,
"detour": "another-in",
"sniff": false, "sniff": false,
"sniff_override_destination": false, "sniff_override_destination": false,
"sniff_timeout": "300ms", "sniff_timeout": "300ms",
"domain_strategy": "prefer_ipv6", "domain_strategy": "prefer_ipv6",
"udp_disable_domain_unmapping": false "udp_timeout": 300,
"proxy_protocol": false,
"proxy_protocol_accept_no_header": false,
"detour": "another-in"
} }
``` ```
### Fields ### Fields
| Field | Available Context | | Field | Available Context |
|--------------------------------|-------------------------------------------------------------------| |-----------------------------------|-------------------------------------------------------------------|
| `listen` | Needs to listen on TCP or UDP. | | `listen` | Needs to listen on TCP or UDP. |
| `listen_port` | 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_fast_open` | Needs to listen on TCP. |
| `tcp_multi_path` | 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_timeout` | Needs to assemble UDP connections, currently Tun and Shadowsocks. |
| `udp_disable_domain_unmapping` | Needs to listen on UDP and accept domain UDP addresses. | | `proxy_protocol` | Needs to listen on TCP. |
| `proxy_protocol_accept_no_header` | When `proxy_protocol` enabled |
#### listen #### listen
@@ -54,16 +56,6 @@ Enable TCP Multi Path.
Enable UDP fragmentation. 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 #### sniff
Enable sniffing. Enable sniffing.
@@ -90,10 +82,20 @@ 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. If `sniff_override_destination` is in effect, its value will be taken as a fallback.
#### udp_disable_domain_unmapping #### udp_timeout
If enabled, for UDP proxy requests addressed to a domain, UDP NAT expiration time in seconds, default is 300 (5 minutes).
the original packet address will be sent in the response instead of the mapped domain.
This option is used for compatibility with clients that #### proxy_protocol
do not support receiving UDP packets with domain addresses, such as Surge.
Parse [Proxy Protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) in the connection header.
#### proxy_protocol_accept_no_header
Accept connections without Proxy Protocol header.
#### detour
If set, connections will be forwarded to the specified inbound.
Requires target inbound support, see [Injectable](/configuration/inbound/#fields).

View File

@@ -7,13 +7,14 @@
"tcp_fast_open": false, "tcp_fast_open": false,
"tcp_multi_path": false, "tcp_multi_path": false,
"udp_fragment": false, "udp_fragment": false,
"udp_timeout": 300,
"detour": "another-in",
"sniff": false, "sniff": false,
"sniff_override_destination": false, "sniff_override_destination": false,
"sniff_timeout": "300ms", "sniff_timeout": "300ms",
"domain_strategy": "prefer_ipv6", "domain_strategy": "prefer_ipv6",
"udp_disable_domain_unmapping": false "udp_timeout": 300,
"proxy_protocol": false,
"proxy_protocol_accept_no_header": false,
"detour": "another-in"
} }
``` ```
@@ -25,7 +26,8 @@
| `tcp_fast_open` | 需要监听 TCP。 | | `tcp_fast_open` | 需要监听 TCP。 |
| `tcp_multi_path` | 需要监听 TCP。 | | `tcp_multi_path` | 需要监听 TCP。 |
| `udp_timeout` | 需要组装 UDP 连接, 当前为 Tun 和 Shadowsocks。 | | `udp_timeout` | 需要组装 UDP 连接, 当前为 Tun 和 Shadowsocks。 |
| | `proxy_protocol` | 需要监听 TCP。 |
| `proxy_protocol_accept_no_header` | `proxy_protocol` 启用时 |
### 字段 ### 字段
@@ -55,16 +57,6 @@
启用 UDP 分段。 启用 UDP 分段。
#### udp_timeout
UDP NAT 过期时间,以秒为单位,默认为 3005 分钟)。
#### detour
如果设置,连接将被转发到指定的入站。
需要目标入站支持,参阅 [注入支持](/zh/configuration/inbound/#_3)。
#### sniff #### sniff
启用协议探测。 启用协议探测。
@@ -91,8 +83,20 @@ UDP NAT 过期时间,以秒为单位,默认为 3005 分钟)。
如果 `sniff_override_destination` 生效,它的值将作为后备。 如果 `sniff_override_destination` 生效,它的值将作为后备。
#### udp_disable_domain_unmapping #### udp_timeout
如果启用,对于地址为域的 UDP 代理请求,将在响应中发送原始包地址而不是映射的域 UDP NAT 过期时间,以秒为单位,默认为 3005 分钟)
此选项用于兼容不支持接收带有域地址的 UDP 包的客户端,如 Surge。 #### proxy_protocol
解析连接头中的 [代理协议](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)。
#### proxy_protocol_accept_no_header
接受没有代理协议标头的连接。
#### detour
如果设置,连接将被转发到指定的入站。
需要目标入站支持,参阅 [注入支持](/zh/configuration/inbound/#_3)。

View File

@@ -2,14 +2,12 @@ package clashapi
import ( import (
"bytes" "bytes"
"net"
"net/http" "net/http"
"time" "time"
"github.com/sagernet/sing-box/common/json" "github.com/sagernet/sing-box/common/json"
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol" "github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
"github.com/sagernet/ws" "github.com/sagernet/websocket"
"github.com/sagernet/ws/wsutil"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/render" "github.com/go-chi/render"
@@ -29,16 +27,16 @@ type Memory struct {
func memory(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) { func memory(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
var conn net.Conn var wsConn *websocket.Conn
if r.Header.Get("Upgrade") == "websocket" { if websocket.IsWebSocketUpgrade(r) {
var err error var err error
conn, _, _, err = ws.UpgradeHTTP(r, w) wsConn, err = upgrader.Upgrade(w, r, nil)
if err != nil { if err != nil {
return return
} }
} }
if conn == nil { if wsConn == nil {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
render.Status(r, http.StatusOK) render.Status(r, http.StatusOK)
} }
@@ -65,12 +63,13 @@ func memory(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r
}); err != nil { }); err != nil {
break break
} }
if conn == nil { if wsConn == nil {
_, err = w.Write(buf.Bytes()) _, err = w.Write(buf.Bytes())
w.(http.Flusher).Flush() w.(http.Flusher).Flush()
} else { } else {
err = wsutil.WriteServerText(conn, buf.Bytes()) err = wsConn.WriteMessage(websocket.TextMessage, buf.Bytes())
} }
if err != nil { if err != nil {
break break
} }

View File

@@ -9,8 +9,7 @@ import (
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/json" "github.com/sagernet/sing-box/common/json"
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol" "github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
"github.com/sagernet/ws" "github.com/sagernet/websocket"
"github.com/sagernet/ws/wsutil"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/render" "github.com/go-chi/render"
@@ -26,13 +25,13 @@ func connectionRouter(router adapter.Router, trafficManager *trafficontrol.Manag
func getConnections(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) { func getConnections(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Upgrade") != "websocket" { if !websocket.IsWebSocketUpgrade(r) {
snapshot := trafficManager.Snapshot() snapshot := trafficManager.Snapshot()
render.JSON(w, r, snapshot) render.JSON(w, r, snapshot)
return return
} }
conn, _, _, err := ws.UpgradeHTTP(r, w) conn, err := upgrader.Upgrade(w, r, nil)
if err != nil { if err != nil {
return return
} }
@@ -57,7 +56,7 @@ func getConnections(trafficManager *trafficontrol.Manager) func(w http.ResponseW
if err := json.NewEncoder(buf).Encode(snapshot); err != nil { if err := json.NewEncoder(buf).Encode(snapshot); err != nil {
return err return err
} }
return wsutil.WriteServerText(conn, buf.Bytes()) return conn.WriteMessage(websocket.TextMessage, buf.Bytes())
} }
if err = sendSnapshot(); err != nil { if err = sendSnapshot(); err != nil {

View File

@@ -25,8 +25,7 @@ import (
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/service" "github.com/sagernet/sing/service"
"github.com/sagernet/sing/service/filemanager" "github.com/sagernet/sing/service/filemanager"
"github.com/sagernet/ws" "github.com/sagernet/websocket"
"github.com/sagernet/ws/wsutil"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/cors" "github.com/go-chi/cors"
@@ -315,7 +314,7 @@ func authentication(serverSecret string) func(next http.Handler) http.Handler {
} }
// Browser websocket not support custom header // Browser websocket not support custom header
if r.Header.Get("Upgrade") == "websocket" && r.URL.Query().Get("token") != "" { if websocket.IsWebSocketUpgrade(r) && r.URL.Query().Get("token") != "" {
token := r.URL.Query().Get("token") token := r.URL.Query().Get("token")
if token != serverSecret { if token != serverSecret {
render.Status(r, http.StatusUnauthorized) render.Status(r, http.StatusUnauthorized)
@@ -352,6 +351,12 @@ 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 { type Traffic struct {
Up int64 `json:"up"` Up int64 `json:"up"`
Down int64 `json:"down"` Down int64 `json:"down"`
@@ -359,17 +364,16 @@ type Traffic struct {
func traffic(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) { func traffic(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
var conn net.Conn var wsConn *websocket.Conn
if r.Header.Get("Upgrade") == "websocket" { if websocket.IsWebSocketUpgrade(r) {
var err error var err error
conn, _, _, err = ws.UpgradeHTTP(r, w) wsConn, err = upgrader.Upgrade(w, r, nil)
if err != nil { if err != nil {
return return
} }
defer conn.Close()
} }
if conn == nil { if wsConn == nil {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
render.Status(r, http.StatusOK) render.Status(r, http.StatusOK)
} }
@@ -388,11 +392,11 @@ func traffic(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter,
break break
} }
if conn == nil { if wsConn == nil {
_, err = w.Write(buf.Bytes()) _, err = w.Write(buf.Bytes())
w.(http.Flusher).Flush() w.(http.Flusher).Flush()
} else { } else {
err = wsutil.WriteServerText(conn, buf.Bytes()) err = wsConn.WriteMessage(websocket.TextMessage, buf.Bytes())
} }
if err != nil { if err != nil {
@@ -428,16 +432,16 @@ func getLogs(logFactory log.ObservableFactory) func(w http.ResponseWriter, r *ht
} }
defer logFactory.UnSubscribe(subscription) defer logFactory.UnSubscribe(subscription)
var conn net.Conn var wsConn *websocket.Conn
if r.Header.Get("Upgrade") == "websocket" { if websocket.IsWebSocketUpgrade(r) {
conn, _, _, err = ws.UpgradeHTTP(r, w) var err error
wsConn, err = upgrader.Upgrade(w, r, nil)
if err != nil { if err != nil {
return return
} }
defer conn.Close()
} }
if conn == nil { if wsConn == nil {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
render.Status(r, http.StatusOK) render.Status(r, http.StatusOK)
} }
@@ -461,11 +465,11 @@ func getLogs(logFactory log.ObservableFactory) func(w http.ResponseWriter, r *ht
if err != nil { if err != nil {
break break
} }
if conn == nil { if wsConn == nil {
_, err = w.Write(buf.Bytes()) _, err = w.Write(buf.Bytes())
w.(http.Flusher).Flush() w.(http.Flusher).Flush()
} else { } else {
err = wsutil.WriteServerText(conn, buf.Bytes()) err = wsConn.WriteMessage(websocket.TextMessage, buf.Bytes())
} }
if err != nil { if err != nil {

View File

@@ -115,11 +115,7 @@ func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions
if len(options.IncludeAndroidUser) > 0 { if len(options.IncludeAndroidUser) > 0 {
return nil, E.New("android: unsupported android_user option") return nil, E.New("android: unsupported android_user option")
} }
routeRanges, err := options.BuildAutoRouteRanges() tunFd, err := w.iif.OpenTun(&tunOptions{options, platformOptions})
if err != nil {
return nil, err
}
tunFd, err := w.iif.OpenTun(&tunOptions{options, routeRanges, platformOptions})
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -60,7 +60,6 @@ var _ TunOptions = (*tunOptions)(nil)
type tunOptions struct { type tunOptions struct {
*tun.Options *tun.Options
routeRanges []netip.Prefix
option.TunPlatformOptions option.TunPlatformOptions
} }
@@ -92,15 +91,11 @@ func (o *tunOptions) GetStrictRoute() bool {
} }
func (o *tunOptions) GetInet4RouteAddress() RoutePrefixIterator { func (o *tunOptions) GetInet4RouteAddress() RoutePrefixIterator {
return mapRoutePrefix(common.Filter(o.routeRanges, func(it netip.Prefix) bool { return mapRoutePrefix(o.Inet4RouteAddress)
return it.Addr().Is4()
}))
} }
func (o *tunOptions) GetInet6RouteAddress() RoutePrefixIterator { func (o *tunOptions) GetInet6RouteAddress() RoutePrefixIterator {
return mapRoutePrefix(common.Filter(o.routeRanges, func(it netip.Prefix) bool { return mapRoutePrefix(o.Inet6RouteAddress)
return it.Addr().Is6()
}))
} }
func (o *tunOptions) GetIncludePackage() StringIterator { func (o *tunOptions) GetIncludePackage() StringIterator {

11
go.mod
View File

@@ -26,20 +26,20 @@ require (
github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab 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.0.0-20231008035953-32727fef9460
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
github.com/sagernet/sing v0.2.16-0.20231028125948-afcc9cb766c2 github.com/sagernet/sing v0.2.16-0.20231021090846-8002db54c028
github.com/sagernet/sing-dns v0.1.10 github.com/sagernet/sing-dns v0.1.10
github.com/sagernet/sing-mux v0.1.3 github.com/sagernet/sing-mux v0.1.3
github.com/sagernet/sing-quic v0.1.3-0.20231026034240-fa3d997246b6 github.com/sagernet/sing-quic v0.1.3-0.20231026034240-fa3d997246b6
github.com/sagernet/sing-shadowsocks v0.2.5 github.com/sagernet/sing-shadowsocks v0.2.5
github.com/sagernet/sing-shadowsocks2 v0.1.4 github.com/sagernet/sing-shadowsocks2 v0.1.4
github.com/sagernet/sing-shadowtls v0.1.4 github.com/sagernet/sing-shadowtls v0.1.4
github.com/sagernet/sing-tun v0.1.17-0.20231030120513-2e85725657c1 github.com/sagernet/sing-tun v0.1.17-0.20231026060825-efd9884154a6
github.com/sagernet/sing-vmess v0.1.8 github.com/sagernet/sing-vmess v0.1.8
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 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/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f
github.com/sagernet/ws v0.0.0-20231030053741-7d481eb31bed
github.com/spf13/cobra v1.7.0 github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
go.uber.org/zap v1.26.0 go.uber.org/zap v1.26.0
@@ -59,17 +59,16 @@ require (
github.com/ajg/form v1.5.1 // indirect github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect github.com/andybalholm/brotli v1.0.5 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/getsentry/sentry-go v0.25.0 // indirect
github.com/go-ole/go-ole v1.3.0 // 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/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/golang/protobuf v1.5.3 // indirect
github.com/google/btree v1.1.2 // 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-20210407192527-94a9f03dee38 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect github.com/hashicorp/yamux v0.1.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/native v1.1.0 // indirect github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/compress v1.15.15 // indirect github.com/klauspost/compress v1.16.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/libdns/libdns v0.2.1 // indirect github.com/libdns/libdns v0.2.1 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect

20
go.sum
View File

@@ -20,6 +20,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/getsentry/sentry-go v0.25.0 h1:q6Eo+hS+yoJlTO3uu/azhQadsD8V+jQn2D8VvX1eOyI=
github.com/getsentry/sentry-go v0.25.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
@@ -31,10 +33,6 @@ 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-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 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= 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 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= 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= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
@@ -59,6 +57,7 @@ github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtL
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
@@ -114,8 +113,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/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.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.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
github.com/sagernet/sing v0.2.16-0.20231028125948-afcc9cb766c2 h1:PW18IgRodvppd09d4mewYM3Hedu3PtFERN8yOqkTVk0= github.com/sagernet/sing v0.2.16-0.20231021090846-8002db54c028 h1:6GbQt7SC9y5Imrq5jDMbXDSaNiMhJ8KBjhjtQRuqQvE=
github.com/sagernet/sing v0.2.16-0.20231028125948-afcc9cb766c2/go.mod h1:AhNEHu0GXrpqkuzvTwvC8+j2cQUU/dh+zLEmq4C99pg= 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 h1:iIU7nRBlUYj+fF2TaktGIvRiTFFrHwSMedLQsvlTZCI=
github.com/sagernet/sing-dns v0.1.10/go.mod h1:vtUimtf7Nq9EdvD5WTpfCr69KL1M7bcgOVKiYBiAY/c= 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 h1:fAf7PZa2A55mCeh0KKM02f1k2Y4vEmxuZZ/51ahkkLA=
@@ -128,8 +127,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-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 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
github.com/sagernet/sing-tun v0.1.17-0.20231030120513-2e85725657c1 h1:QxC+myHDZ0BnkIEqXE0lWUzfYEVlhhQdSCo7mOMm7x4= github.com/sagernet/sing-tun v0.1.17-0.20231026060825-efd9884154a6 h1:4yEXBqQoUgXj7qPSLD6lr+z9/KfsvixO9JUA2i5xnM8=
github.com/sagernet/sing-tun v0.1.17-0.20231030120513-2e85725657c1/go.mod h1:4ACZp3C6TDSy1rsMrfwtSyLrKPtm9Wm2eKHwhYIojbU= github.com/sagernet/sing-tun v0.1.17-0.20231026060825-efd9884154a6/go.mod h1:w2+S+uWE94E/pQWSDdDdMIjwAEb645kuGPunr6ZllUg=
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc= 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/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
@@ -138,10 +137,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/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 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4=
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM= 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 h1:Kvo8w8Y9lzFGB/7z09MJ3TR99TFtfI/IuY87Ygcycho=
github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f/go.mod h1:mySs0abhpc/gLlvhoq7HP1RzOaRmIXVeZGCh++zoApk= 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 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg=
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s= github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
@@ -193,7 +192,6 @@ 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.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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.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.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= 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.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

View File

@@ -71,25 +71,23 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
logger: logger, logger: logger,
inboundOptions: options.InboundOptions, inboundOptions: options.InboundOptions,
tunOptions: tun.Options{ tunOptions: tun.Options{
Name: options.InterfaceName, Name: options.InterfaceName,
MTU: tunMTU, MTU: tunMTU,
Inet4Address: options.Inet4Address, Inet4Address: options.Inet4Address,
Inet6Address: options.Inet6Address, Inet6Address: options.Inet6Address,
AutoRoute: options.AutoRoute, AutoRoute: options.AutoRoute,
StrictRoute: options.StrictRoute, StrictRoute: options.StrictRoute,
IncludeInterface: options.IncludeInterface, IncludeInterface: options.IncludeInterface,
ExcludeInterface: options.ExcludeInterface, ExcludeInterface: options.ExcludeInterface,
Inet4RouteAddress: options.Inet4RouteAddress, Inet4RouteAddress: options.Inet4RouteAddress,
Inet6RouteAddress: options.Inet6RouteAddress, Inet6RouteAddress: options.Inet6RouteAddress,
Inet4RouteExcludeAddress: options.Inet4RouteExcludeAddress, IncludeUID: includeUID,
Inet6RouteExcludeAddress: options.Inet6RouteExcludeAddress, ExcludeUID: excludeUID,
IncludeUID: includeUID, IncludeAndroidUser: options.IncludeAndroidUser,
ExcludeUID: excludeUID, IncludePackage: options.IncludePackage,
IncludeAndroidUser: options.IncludeAndroidUser, ExcludePackage: options.ExcludePackage,
IncludePackage: options.IncludePackage, InterfaceMonitor: router.InterfaceMonitor(),
ExcludePackage: options.ExcludePackage, TableIndex: 2022,
InterfaceMonitor: router.InterfaceMonitor(),
TableIndex: 2022,
}, },
endpointIndependentNat: options.EndpointIndependentNat, endpointIndependentNat: options.EndpointIndependentNat,
udpTimeout: udpTimeout, udpTimeout: udpTimeout,

View File

@@ -120,11 +120,10 @@ func (h *Inbound) UnmarshalJSON(bytes []byte) error {
} }
type InboundOptions struct { type InboundOptions struct {
SniffEnabled bool `json:"sniff,omitempty"` SniffEnabled bool `json:"sniff,omitempty"`
SniffOverrideDestination bool `json:"sniff_override_destination,omitempty"` SniffOverrideDestination bool `json:"sniff_override_destination,omitempty"`
SniffTimeout Duration `json:"sniff_timeout,omitempty"` SniffTimeout Duration `json:"sniff_timeout,omitempty"`
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"` DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
} }
type ListenOptions struct { type ListenOptions struct {

View File

@@ -3,28 +3,26 @@ package option
import "net/netip" import "net/netip"
type TunInboundOptions struct { type TunInboundOptions struct {
InterfaceName string `json:"interface_name,omitempty"` InterfaceName string `json:"interface_name,omitempty"`
MTU uint32 `json:"mtu,omitempty"` MTU uint32 `json:"mtu,omitempty"`
Inet4Address Listable[netip.Prefix] `json:"inet4_address,omitempty"` Inet4Address Listable[netip.Prefix] `json:"inet4_address,omitempty"`
Inet6Address Listable[netip.Prefix] `json:"inet6_address,omitempty"` Inet6Address Listable[netip.Prefix] `json:"inet6_address,omitempty"`
AutoRoute bool `json:"auto_route,omitempty"` AutoRoute bool `json:"auto_route,omitempty"`
StrictRoute bool `json:"strict_route,omitempty"` StrictRoute bool `json:"strict_route,omitempty"`
Inet4RouteAddress Listable[netip.Prefix] `json:"inet4_route_address,omitempty"` Inet4RouteAddress Listable[netip.Prefix] `json:"inet4_route_address,omitempty"`
Inet6RouteAddress Listable[netip.Prefix] `json:"inet6_route_address,omitempty"` Inet6RouteAddress Listable[netip.Prefix] `json:"inet6_route_address,omitempty"`
Inet4RouteExcludeAddress Listable[netip.Prefix] `json:"inet4_route_exclude_address,omitempty"` IncludeInterface Listable[string] `json:"include_interface,omitempty"`
Inet6RouteExcludeAddress Listable[netip.Prefix] `json:"inet6_route_exclude_address,omitempty"` ExcludeInterface Listable[string] `json:"exclude_interface,omitempty"`
IncludeInterface Listable[string] `json:"include_interface,omitempty"` IncludeUID Listable[uint32] `json:"include_uid,omitempty"`
ExcludeInterface Listable[string] `json:"exclude_interface,omitempty"` IncludeUIDRange Listable[string] `json:"include_uid_range,omitempty"`
IncludeUID Listable[uint32] `json:"include_uid,omitempty"` ExcludeUID Listable[uint32] `json:"exclude_uid,omitempty"`
IncludeUIDRange Listable[string] `json:"include_uid_range,omitempty"` ExcludeUIDRange Listable[string] `json:"exclude_uid_range,omitempty"`
ExcludeUID Listable[uint32] `json:"exclude_uid,omitempty"` IncludeAndroidUser Listable[int] `json:"include_android_user,omitempty"`
ExcludeUIDRange Listable[string] `json:"exclude_uid_range,omitempty"` IncludePackage Listable[string] `json:"include_package,omitempty"`
IncludeAndroidUser Listable[int] `json:"include_android_user,omitempty"` ExcludePackage Listable[string] `json:"exclude_package,omitempty"`
IncludePackage Listable[string] `json:"include_package,omitempty"` EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"`
ExcludePackage Listable[string] `json:"exclude_package,omitempty"` UDPTimeout int64 `json:"udp_timeout,omitempty"`
EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"` Stack string `json:"stack,omitempty"`
UDPTimeout int64 `json:"udp_timeout,omitempty"` Platform *TunPlatformOptions `json:"platform,omitempty"`
Stack string `json:"stack,omitempty"`
Platform *TunPlatformOptions `json:"platform,omitempty"`
InboundOptions InboundOptions
} }

View File

@@ -121,13 +121,9 @@ func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn,
} }
if destinationAddress.IsValid() { if destinationAddress.IsValid() {
if metadata.Destination.IsFqdn() { if metadata.Destination.IsFqdn() {
if metadata.InboundOptions.UDPDisableDomainUnmapping { outConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination)
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) natConn.UpdateDestination(destinationAddress)
} }
} }
@@ -170,7 +166,7 @@ func NewDirectPacketConnection(ctx context.Context, router adapter.Router, this
if metadata.Destination.IsFqdn() { if metadata.Destination.IsFqdn() {
outConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination) 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) natConn.UpdateDestination(destinationAddress)
} }
} }

View File

@@ -47,7 +47,7 @@ require (
github.com/hashicorp/yamux v0.1.1 // indirect github.com/hashicorp/yamux v0.1.1 // indirect
github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c // indirect github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c // indirect
github.com/josharian/native v1.1.0 // indirect github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/compress v1.15.15 // indirect github.com/klauspost/compress v1.16.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/libdns/alidns v1.0.3 // indirect github.com/libdns/alidns v1.0.3 // indirect
github.com/libdns/cloudflare v0.1.0 // indirect github.com/libdns/cloudflare v0.1.0 // indirect

View File

@@ -68,6 +68,7 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=

View File

@@ -5,36 +5,58 @@ import (
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
"strings"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/tls" "github.com/sagernet/sing-box/common/tls"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
sHTTP "github.com/sagernet/sing/protocol/http" sHTTP "github.com/sagernet/sing/protocol/http"
"github.com/sagernet/ws" "github.com/sagernet/websocket"
) )
var _ adapter.V2RayClientTransport = (*Client)(nil) var _ adapter.V2RayClientTransport = (*Client)(nil)
type Client struct { type Client struct {
dialer N.Dialer dialer *websocket.Dialer
tlsConfig tls.Config
serverAddr M.Socksaddr
requestURL url.URL requestURL url.URL
requestURLString string
headers http.Header headers http.Header
maxEarlyData uint32 maxEarlyData uint32
earlyDataHeaderName string earlyDataHeaderName string
} }
func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayWebsocketOptions, tlsConfig tls.Config) adapter.V2RayClientTransport { 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,
}
if tlsConfig != nil { if tlsConfig != nil {
if len(tlsConfig.NextProtos()) == 0 { if len(tlsConfig.NextProtos()) == 0 {
tlsConfig.SetNextProtos([]string{"http/1.1"}) 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 var requestURL url.URL
if tlsConfig == nil { if tlsConfig == nil {
@@ -48,57 +70,35 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
if err != nil { if err != nil {
return nil return nil
} }
if !strings.HasPrefix(requestURL.Path, "/") {
requestURL.Path = "/" + requestURL.Path
}
headers := make(http.Header) headers := make(http.Header)
for key, value := range options.Headers { for key, value := range options.Headers {
headers[key] = value headers[key] = value
} }
return &Client{ return &Client{
dialer, wsDialer,
tlsConfig,
serverAddr,
requestURL, requestURL,
requestURL.String(),
headers, headers,
options.MaxEarlyData, options.MaxEarlyData,
options.EarlyDataHeaderName, options.EarlyDataHeaderName,
} }
} }
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) { func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
if c.maxEarlyData <= 0 { if c.maxEarlyData <= 0 {
conn, err := c.dialContext(ctx, &c.requestURL, c.headers) conn, response, err := c.dialer.DialContext(ctx, c.requestURLString, c.headers)
if err != nil { if err == nil {
return nil, err return &WebsocketConn{Conn: conn, Writer: NewWriter(conn, false)}, nil
} }
return conn, nil return nil, wrapDialError(response, err)
} else { } else {
return &EarlyWebsocketConn{Client: c, ctx: ctx, create: make(chan struct{})}, nil 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)
}

View File

@@ -1,11 +1,11 @@
package v2raywebsocket package v2raywebsocket
import ( import (
"bufio"
"context" "context"
"encoding/base64" "encoding/base64"
"io" "io"
"net" "net"
"net/http"
"os" "os"
"sync" "sync"
"time" "time"
@@ -13,96 +13,50 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/debug"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/ws" "github.com/sagernet/websocket"
"github.com/sagernet/ws/wsutil"
) )
type WebsocketConn struct { type WebsocketConn struct {
net.Conn *websocket.Conn
*Writer *Writer
state ws.State remoteAddr net.Addr
reader *wsutil.Reader reader io.Reader
controlHandler wsutil.FrameHandlerFunc
remoteAddr net.Addr
} }
func NewConn(conn net.Conn, br *bufio.Reader, remoteAddr net.Addr, state ws.State) *WebsocketConn { func NewServerConn(wsConn *websocket.Conn, remoteAddr net.Addr) *WebsocketConn {
controlHandler := wsutil.ControlFrameHandler(conn, state)
var reader io.Reader
if br != nil && br.Buffered() > 0 {
reader = br
} else {
reader = conn
}
return &WebsocketConn{ return &WebsocketConn{
Conn: conn, Conn: wsConn,
state: state, remoteAddr: remoteAddr,
reader: &wsutil.Reader{ Writer: NewWriter(wsConn, true),
Source: reader,
State: state,
SkipHeaderCheck: !debug.Enabled,
OnIntermediate: controlHandler,
},
controlHandler: controlHandler,
remoteAddr: remoteAddr,
Writer: NewWriter(conn, state),
} }
} }
func (c *WebsocketConn) Close() error { func (c *WebsocketConn) Close() error {
c.Conn.SetWriteDeadline(time.Now().Add(C.TCPTimeout)) err := c.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""), time.Now().Add(C.TCPTimeout))
frame := ws.NewCloseFrame(ws.NewCloseFrameBody( if err != nil {
ws.StatusNormalClosure, "", return c.Conn.Close()
))
if c.state == ws.StateClientSide {
frame = ws.MaskFrameInPlace(frame)
} }
ws.WriteFrame(c.Conn, frame)
c.Conn.Close()
return nil return nil
} }
func (c *WebsocketConn) Read(b []byte) (n int, err error) { func (c *WebsocketConn) Read(b []byte) (n int, err error) {
var header ws.Header
for { for {
if c.reader == nil {
_, c.reader, err = c.NextReader()
if err != nil {
err = wrapError(err)
return
}
}
n, err = c.reader.Read(b) n, err = c.reader.Read(b)
if n > 0 { if E.IsMulti(err, io.EOF) {
err = nil c.reader = 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 {
return
}
continue continue
} }
if header.OpCode&ws.OpBinary == 0 { err = wrapError(err)
err = c.reader.Discard()
if err != nil {
return
}
continue
}
}
}
func (c *WebsocketConn) Write(p []byte) (n int, err error) {
err = wsutil.WriteMessage(c.Conn, c.state, ws.OpBinary, p)
if err != nil {
return return
} }
n = len(p)
return
} }
func (c *WebsocketConn) RemoteAddr() net.Addr { func (c *WebsocketConn) RemoteAddr() net.Addr {
@@ -129,7 +83,11 @@ func (c *WebsocketConn) NeedAdditionalReadDeadline() bool {
} }
func (c *WebsocketConn) Upstream() any { func (c *WebsocketConn) Upstream() any {
return c.Conn return c.Conn.NetConn()
}
func (c *WebsocketConn) UpstreamWriter() any {
return c.Writer
} }
type EarlyWebsocketConn struct { type EarlyWebsocketConn struct {
@@ -155,7 +113,8 @@ func (c *EarlyWebsocketConn) writeRequest(content []byte) error {
var ( var (
earlyData []byte earlyData []byte
lateData []byte lateData []byte
conn *WebsocketConn conn *websocket.Conn
response *http.Response
err error err error
) )
if len(content) > int(c.maxEarlyData) { if len(content) > int(c.maxEarlyData) {
@@ -169,16 +128,19 @@ func (c *EarlyWebsocketConn) writeRequest(content []byte) error {
if c.earlyDataHeaderName == "" { if c.earlyDataHeaderName == "" {
requestURL := c.requestURL requestURL := c.requestURL
requestURL.Path += earlyDataString requestURL.Path += earlyDataString
conn, err = c.dialContext(c.ctx, &requestURL, c.headers) conn, response, err = c.dialer.DialContext(c.ctx, requestURL.String(), c.headers)
} else { } else {
headers := c.headers.Clone() headers := c.headers.Clone()
headers.Set(c.earlyDataHeaderName, earlyDataString) headers.Set(c.earlyDataHeaderName, earlyDataString)
conn, err = c.dialContext(c.ctx, &c.requestURL, headers) conn, response, err = c.dialer.DialContext(c.ctx, c.requestURLString, headers)
} }
} else { } else {
conn, err = c.dialContext(c.ctx, &c.requestURL, c.headers) conn, response, err = c.dialer.DialContext(c.ctx, c.requestURLString, c.headers)
} }
c.conn = conn if err != nil {
return wrapDialError(response, err)
}
c.conn = &WebsocketConn{Conn: conn, Writer: NewWriter(conn, false)}
if len(lateData) > 0 { if len(lateData) > 0 {
_, err = c.conn.Write(lateData) _, err = c.conn.Write(lateData)
} }
@@ -262,3 +224,13 @@ func (c *EarlyWebsocketConn) Upstream() any {
func (c *EarlyWebsocketConn) LazyHeadroom() bool { func (c *EarlyWebsocketConn) LazyHeadroom() bool {
return c.conn == nil 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
}

View File

@@ -0,0 +1,6 @@
package v2raywebsocket
import _ "unsafe"
//go:linkname maskBytes github.com/sagernet/websocket.maskBytes
func maskBytes(key [4]byte, pos int, b []byte) int

View File

@@ -20,7 +20,7 @@ import (
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
aTLS "github.com/sagernet/sing/common/tls" aTLS "github.com/sagernet/sing/common/tls"
sHttp "github.com/sagernet/sing/protocol/http" sHttp "github.com/sagernet/sing/protocol/http"
"github.com/sagernet/ws" "github.com/sagernet/websocket"
) )
var _ adapter.V2RayServerTransport = (*Server)(nil) var _ adapter.V2RayServerTransport = (*Server)(nil)
@@ -58,6 +58,13 @@ func NewServer(ctx context.Context, options option.V2RayWebsocketOptions, tlsCon
return server, nil 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) { func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
if s.maxEarlyData == 0 || s.earlyDataHeaderName != "" { if s.maxEarlyData == 0 || s.earlyDataHeaderName != "" {
if request.URL.Path != s.path { if request.URL.Path != s.path {
@@ -88,14 +95,14 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
s.invalidRequest(writer, request, http.StatusBadRequest, E.Cause(err, "decode early data")) s.invalidRequest(writer, request, http.StatusBadRequest, E.Cause(err, "decode early data"))
return return
} }
wsConn, reader, _, err := ws.UpgradeHTTP(request, writer) wsConn, err := upgrader.Upgrade(writer, request, nil)
if err != nil { if err != nil {
s.invalidRequest(writer, request, 0, E.Cause(err, "upgrade websocket connection")) s.invalidRequest(writer, request, 0, E.Cause(err, "upgrade websocket connection"))
return return
} }
var metadata M.Metadata var metadata M.Metadata
metadata.Source = sHttp.SourceAddress(request) metadata.Source = sHttp.SourceAddress(request)
conn = NewConn(wsConn, reader.Reader, metadata.Source.TCPAddr(), ws.StateServerSide) conn = NewServerConn(wsConn, metadata.Source.TCPAddr())
if len(earlyData) > 0 { if len(earlyData) > 0 {
conn = bufio.NewCachedConn(conn, buf.As(earlyData)) conn = bufio.NewCachedConn(conn, buf.As(earlyData))
} }

View File

@@ -2,27 +2,36 @@ package v2raywebsocket
import ( import (
"encoding/binary" "encoding/binary"
"io"
"math/rand" "math/rand"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/ws" "github.com/sagernet/websocket"
) )
type Writer struct { type Writer struct {
*websocket.Conn
writer N.ExtendedWriter writer N.ExtendedWriter
isServer bool isServer bool
} }
func NewWriter(writer io.Writer, state ws.State) *Writer { func NewWriter(conn *websocket.Conn, isServer bool) *Writer {
return &Writer{ return &Writer{
bufio.NewExtendedWriter(writer), conn,
state == ws.StateServerSide, bufio.NewExtendedWriter(conn.NetConn()),
isServer,
} }
} }
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 { func (w *Writer) WriteBuffer(buffer *buf.Buffer) error {
var payloadBitLength int var payloadBitLength int
dataLen := buffer.Len() dataLen := buffer.Len()
@@ -43,7 +52,7 @@ func (w *Writer) WriteBuffer(buffer *buf.Buffer) error {
} }
header := buffer.ExtendHeader(headerLen) header := buffer.ExtendHeader(headerLen)
header[0] = byte(ws.OpBinary) | 0x80 header[0] = websocket.BinaryMessage | 1<<7
if w.isServer { if w.isServer {
header[1] = 0 header[1] = 0
} else { } else {
@@ -63,12 +72,16 @@ func (w *Writer) WriteBuffer(buffer *buf.Buffer) error {
if !w.isServer { if !w.isServer {
maskKey := rand.Uint32() maskKey := rand.Uint32()
binary.BigEndian.PutUint32(header[1+payloadBitLength:], maskKey) binary.BigEndian.PutUint32(header[1+payloadBitLength:], maskKey)
ws.Cipher(data, *(*[4]byte)(header[1+payloadBitLength:]), 0) maskBytes(*(*[4]byte)(header[1+payloadBitLength:]), 0, data)
} }
return w.writer.WriteBuffer(buffer) return w.writer.WriteBuffer(buffer)
} }
func (w *Writer) Upstream() any {
return w.Conn.NetConn()
}
func (w *Writer) FrontHeadroom() int { func (w *Writer) FrontHeadroom() int {
return 14 return 14
} }