mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-12 01:57:18 +10:00
Compare commits
17 Commits
dev-daemon
...
v1.0-beta1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0348ace253 | ||
|
|
c5e38203eb | ||
|
|
9ac31d0233 | ||
|
|
9d8d1cd69d | ||
|
|
07a0381f8b | ||
|
|
f841459004 | ||
|
|
78a26fc139 | ||
|
|
9f6628445e | ||
|
|
fa017b5977 | ||
|
|
58f4a970f2 | ||
|
|
021aa8faed | ||
|
|
83f6e037d6 | ||
|
|
baf153434d | ||
|
|
d481bd7993 | ||
|
|
e859c0a6ef | ||
|
|
59a39e66b1 | ||
|
|
fd5ac69a35 |
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,6 +1,5 @@
|
||||
name: Bug Report
|
||||
description: "Create a report to help us improve."
|
||||
labels: [ bug ]
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
@@ -56,7 +55,7 @@ body:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: config
|
||||
id: log
|
||||
attributes:
|
||||
label: Server and client log file
|
||||
value: |-
|
||||
|
||||
11
Makefile
11
Makefile
@@ -1,6 +1,6 @@
|
||||
NAME = sing-box
|
||||
COMMIT = $(shell git rev-parse --short HEAD)
|
||||
TAGS ?= with_quic,with_wireguard,with_clash_api,with_daemon
|
||||
TAGS ?= with_quic,with_wireguard,with_clash_api
|
||||
PARAMS = -v -trimpath -tags '$(TAGS)' -ldflags \
|
||||
'-X "github.com/sagernet/sing-box/constant.Commit=$(COMMIT)" \
|
||||
-w -s -buildid='
|
||||
@@ -49,7 +49,14 @@ snapshot:
|
||||
ghr --delete --draft --prerelease -p 1 nightly dist/release
|
||||
rm -r dist
|
||||
|
||||
snapshot_install:
|
||||
release:
|
||||
goreleaser release --rm-dist --skip-publish
|
||||
mkdir dist/release
|
||||
mv dist/*.tar.gz dist/*.zip dist/*.deb dist/*.rpm dist/release
|
||||
ghr --delete --draft --prerelease -p 3 $(shell git describe --tags) dist/release
|
||||
rm -r dist
|
||||
|
||||
release_install:
|
||||
go install -v github.com/goreleaser/goreleaser@latest
|
||||
go install -v github.com/tcnksm/ghr@latest
|
||||
|
||||
|
||||
@@ -1,272 +0,0 @@
|
||||
//go:build with_daemon
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/common/json"
|
||||
"github.com/sagernet/sing-box/experimental/daemon"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var commandDaemon = &cobra.Command{
|
||||
Use: "daemon",
|
||||
}
|
||||
|
||||
func init() {
|
||||
commandDaemon.AddCommand(commandDaemonInstall)
|
||||
commandDaemon.AddCommand(commandDaemonUninstall)
|
||||
commandDaemon.AddCommand(commandDaemonStart)
|
||||
commandDaemon.AddCommand(commandDaemonStop)
|
||||
commandDaemon.AddCommand(commandDaemonRestart)
|
||||
commandDaemon.AddCommand(commandDaemonRun)
|
||||
mainCommand.AddCommand(commandDaemon)
|
||||
mainCommand.AddCommand(commandStart)
|
||||
mainCommand.AddCommand(commandStop)
|
||||
mainCommand.AddCommand(commandStatus)
|
||||
}
|
||||
|
||||
var commandDaemonInstall = &cobra.Command{
|
||||
Use: "install",
|
||||
Short: "Install daemon",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := installDaemon()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
|
||||
var commandDaemonUninstall = &cobra.Command{
|
||||
Use: "uninstall",
|
||||
Short: "Uninstall daemon",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := uninstallDaemon()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
|
||||
var commandDaemonStart = &cobra.Command{
|
||||
Use: "start",
|
||||
Short: "Start daemon",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := startDaemon()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
|
||||
var commandDaemonStop = &cobra.Command{
|
||||
Use: "stop",
|
||||
Short: "Stop daemon",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := stopDaemon()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
|
||||
var commandDaemonRestart = &cobra.Command{
|
||||
Use: "restart",
|
||||
Short: "Restart daemon",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := restartDaemon()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
|
||||
var commandDaemonRun = &cobra.Command{
|
||||
Use: "run",
|
||||
Short: "Run daemon",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := runDaemon()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
|
||||
func installDaemon() error {
|
||||
instance, err := daemon.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return instance.Install()
|
||||
}
|
||||
|
||||
func uninstallDaemon() error {
|
||||
instance, err := daemon.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return instance.Uninstall()
|
||||
}
|
||||
|
||||
func startDaemon() error {
|
||||
instance, err := daemon.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return instance.Start()
|
||||
}
|
||||
|
||||
func stopDaemon() error {
|
||||
instance, err := daemon.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return instance.Stop()
|
||||
}
|
||||
|
||||
func restartDaemon() error {
|
||||
instance, err := daemon.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return instance.Restart()
|
||||
}
|
||||
|
||||
func runDaemon() error {
|
||||
instance, err := daemon.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return instance.Run()
|
||||
}
|
||||
|
||||
var commandStart = &cobra.Command{
|
||||
Use: "start",
|
||||
Short: "Start service",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := startService()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
|
||||
var commandStop = &cobra.Command{
|
||||
Use: "stop",
|
||||
Short: "Stop service",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := stopService()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
|
||||
var commandStatus = &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "Check service",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := checkService()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
|
||||
func doRequest(method string, path string, params url.Values, body io.ReadCloser) ([]byte, error) {
|
||||
requestURL := url.URL{
|
||||
Scheme: "http",
|
||||
Path: path,
|
||||
Host: net.JoinHostPort("127.0.0.1", F.ToString(daemon.DefaultDaemonPort)),
|
||||
}
|
||||
if params != nil {
|
||||
requestURL.RawQuery = params.Encode()
|
||||
}
|
||||
request, err := http.NewRequest(method, requestURL.String(), body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := http.DefaultClient.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
var content []byte
|
||||
if response.StatusCode != http.StatusNoContent {
|
||||
content, err = io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if response.StatusCode != http.StatusOK && response.StatusCode != http.StatusNoContent {
|
||||
return nil, E.New(string(content))
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
|
||||
func ping() error {
|
||||
response, err := doRequest("GET", "/ping", nil, nil)
|
||||
if err != nil || string(response) != "pong" {
|
||||
return E.New("daemon not running")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func startService() error {
|
||||
if err := ping(); err != nil {
|
||||
return err
|
||||
}
|
||||
configContent, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return E.Cause(err, "read config")
|
||||
}
|
||||
return common.Error(doRequest("POST", "/run", nil, io.NopCloser(bytes.NewReader(configContent))))
|
||||
}
|
||||
|
||||
func stopService() error {
|
||||
if err := ping(); err != nil {
|
||||
return err
|
||||
}
|
||||
return common.Error(doRequest("GET", "/stop", nil, nil))
|
||||
}
|
||||
|
||||
func checkService() error {
|
||||
if err := ping(); err != nil {
|
||||
return err
|
||||
}
|
||||
response, err := doRequest("GET", "/status", nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var statusResponse daemon.StatusResponse
|
||||
err = json.Unmarshal(response, &statusResponse)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if statusResponse.Running {
|
||||
log.Info("service running")
|
||||
} else {
|
||||
log.Info("service stopped")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -38,7 +38,7 @@ func printVersion(cmd *cobra.Command, args []string) {
|
||||
version += runtime.Version()
|
||||
version += ", "
|
||||
version += runtime.GOOS
|
||||
version += ", "
|
||||
version += "/"
|
||||
version += runtime.GOARCH
|
||||
version += ", "
|
||||
version += "CGO "
|
||||
|
||||
@@ -113,7 +113,10 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
||||
}
|
||||
var bindUDPAddr string
|
||||
udpDialer := dialer
|
||||
bindAddress := netip.Addr(options.BindAddress)
|
||||
var bindAddress netip.Addr
|
||||
if options.BindAddress != nil {
|
||||
bindAddress = options.BindAddress.Build()
|
||||
}
|
||||
if bindAddress.IsValid() {
|
||||
dialer.LocalAddr = &net.TCPAddr{
|
||||
IP: bindAddress.AsSlice(),
|
||||
|
||||
@@ -22,7 +22,7 @@ func StreamDomainNameQuery(readCtx context.Context, reader io.Reader) (*adapter.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if length > 512 {
|
||||
if length == 0 {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
_buffer := buf.StackNewSize(int(length))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package constant
|
||||
|
||||
var (
|
||||
Version = "1.0"
|
||||
Version = "1.0-beta1"
|
||||
Commit = ""
|
||||
)
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
#### 2022/08/24
|
||||
#### 1.0-beta1
|
||||
|
||||
* Initial release
|
||||
|
||||
##### 2022/08/26
|
||||
|
||||
* Fix ipv6 route on linux
|
||||
* Fix read DNS message
|
||||
|
||||
##### 2022/08/25
|
||||
|
||||
* Let vmess use zero instead of auto if TLS enabled
|
||||
* Add trojan fallback for ALPN
|
||||
* Improve ip_cidr rule
|
||||
* Fix format bind_address
|
||||
* Fix http proxy with compressed response
|
||||
* Fix route connections
|
||||
|
||||
##### 2022/08/24
|
||||
|
||||
* Fix naive padding
|
||||
* Fix unix search path
|
||||
@@ -7,7 +25,7 @@
|
||||
* Fix early close on windows and catch any
|
||||
* Initial zh-CN document translation
|
||||
|
||||
#### 2022/08/23
|
||||
##### 2022/08/23
|
||||
|
||||
* Add [V2Ray Transport](/configuration/shared/v2ray-transport) support for VMess and Trojan
|
||||
* Allow plain http request in Naive inbound (It can now be used with nginx)
|
||||
@@ -16,17 +34,17 @@
|
||||
* Parse X-Forward-For in HTTP requests
|
||||
* Handle SIGHUP signal
|
||||
|
||||
#### 2022/08/22
|
||||
##### 2022/08/22
|
||||
|
||||
* Add strategy setting for each [DNS server](/configuration/dns/server)
|
||||
* Add bind address to outbound options
|
||||
|
||||
#### 2022/08/21
|
||||
##### 2022/08/21
|
||||
|
||||
* Add [Tor outbound](/configuration/outbound/tor)
|
||||
* Add [SSH outbound](/configuration/outbound/ssh)
|
||||
|
||||
#### 2022/08/20
|
||||
##### 2022/08/20
|
||||
|
||||
* Attempt to unwrap ip-in-fqdn socksaddr
|
||||
* Fix read packages in android 12
|
||||
@@ -36,52 +54,52 @@
|
||||
* Skip bind connection with private destination to interface
|
||||
* Add [Trojan connection fallback](/configuration/inbound/trojan#fallback)
|
||||
|
||||
#### 2022/08/19
|
||||
##### 2022/08/19
|
||||
|
||||
* Add Hysteria [Inbound](/configuration/inbound/hysteria) and [Outbund](/configuration/outbound/hysteria)
|
||||
* Add [ACME TLS certificate issuer](/configuration/shared/tls)
|
||||
* Allow read config from stdin (-c stdin)
|
||||
* Update gVisor to 20220815.0
|
||||
|
||||
#### 2022/08/18
|
||||
##### 2022/08/18
|
||||
|
||||
* Fix find process with lwip stack
|
||||
* Fix crash on shadowsocks server
|
||||
* Fix crash on darwin tun
|
||||
* Fix write log to file
|
||||
|
||||
#### 2022/08/17
|
||||
##### 2022/08/17
|
||||
|
||||
* Improve async dns transports
|
||||
|
||||
#### 2022/08/16
|
||||
##### 2022/08/16
|
||||
|
||||
* Add ip_version (route/dns) rule item
|
||||
* Add [WireGuard](/configuration/outbound/wireguard) outbound
|
||||
|
||||
#### 2022/08/15
|
||||
##### 2022/08/15
|
||||
|
||||
* Add uid, android user and package rules support in [Tun](/configuration/inbound/tun) routing.
|
||||
|
||||
#### 2022/08/13
|
||||
##### 2022/08/13
|
||||
|
||||
* Fix dns concurrent write
|
||||
|
||||
#### 2022/08/12
|
||||
##### 2022/08/12
|
||||
|
||||
* Performance improvements
|
||||
* Add UoT option for [SOCKS](/configuration/outbound/socks) outbound
|
||||
|
||||
#### 2022/08/11
|
||||
##### 2022/08/11
|
||||
|
||||
* Add UoT option for [Shadowsocks](/configuration/outbound/shadowsocks) outbound, UoT support for all inbounds
|
||||
|
||||
#### 2022/08/10
|
||||
##### 2022/08/10
|
||||
|
||||
* Add full-featured [Naive](/configuration/inbound/naive) inbound
|
||||
* Fix default dns server option [#9] by iKirby
|
||||
|
||||
#### 2022/08/09
|
||||
##### 2022/08/09
|
||||
|
||||
No changelog before.
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# DNS
|
||||
|
||||
### Structure
|
||||
|
||||
```json
|
||||
|
||||
@@ -38,7 +38,8 @@
|
||||
"private"
|
||||
],
|
||||
"source_ip_cidr": [
|
||||
"10.0.0.0/24"
|
||||
"10.0.0.0/24",
|
||||
"192.168.0.1"
|
||||
],
|
||||
"source_port": [
|
||||
12345
|
||||
|
||||
@@ -47,7 +47,7 @@ DNS 服务器的地址。
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含 QUIC 和 HTTP3 传输层,请参阅 [安装](/zh/#installation)。
|
||||
默认安装不包含 QUIC 和 HTTP3 传输层,请参阅 [安装](/zh/#_2)。
|
||||
|
||||
!!! info ""
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# Experimental
|
||||
|
||||
### Structure
|
||||
|
||||
```json
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
!!! error ""
|
||||
|
||||
默认安装不包含 Clash API,参阅 [安装](/zh/#installation)。
|
||||
默认安装不包含 Clash API,参阅 [安装](/zh/#_2)。
|
||||
|
||||
!!! note ""
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含被 Hysteria 依赖的 QUIC,参阅 [安装](/zh/#installation)。
|
||||
默认安装不包含被 Hysteria 依赖的 QUIC,参阅 [安装](/zh/#_2)。
|
||||
|
||||
### Hysteria 字段
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# Inbound
|
||||
|
||||
### Structure
|
||||
|
||||
```json
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含 HTTP3 传输层, 参阅 [安装](/zh/#installation)。
|
||||
默认安装不包含 HTTP3 传输层, 参阅 [安装](/zh/#_2)。
|
||||
|
||||
### Naive 字段
|
||||
|
||||
|
||||
@@ -23,9 +23,15 @@
|
||||
],
|
||||
"tls": {},
|
||||
"fallback": {
|
||||
"server": "127.0.0.0.1",
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 8080
|
||||
},
|
||||
"fallback_for_alpn": {
|
||||
"http/1.1": {
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 8081
|
||||
}
|
||||
},
|
||||
"transport": {}
|
||||
}
|
||||
]
|
||||
@@ -50,7 +56,13 @@ TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
|
||||
|
||||
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.
|
||||
|
||||
Fallback server configuration. Disabled if empty.
|
||||
Fallback server configuration. Disabled if `fallback` and `fallback_for_alpn` are empty.
|
||||
|
||||
#### fallback_for_alpn
|
||||
|
||||
Fallback server configuration for specified ALPN.
|
||||
|
||||
If not empty, TLS fallback requests with ALPN not in this table will be rejected.
|
||||
|
||||
#### transport
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
{
|
||||
"type": "trojan",
|
||||
"tag": "trojan-in",
|
||||
|
||||
"listen": "::",
|
||||
"listen_port": 2080,
|
||||
"tcp_fast_open": false,
|
||||
@@ -14,7 +13,6 @@
|
||||
"sniff_override_destination": false,
|
||||
"domain_strategy": "prefer_ipv6",
|
||||
"proxy_protocol": false,
|
||||
|
||||
"users": [
|
||||
{
|
||||
"name": "sekai",
|
||||
@@ -23,9 +21,15 @@
|
||||
],
|
||||
"tls": {},
|
||||
"fallback": {
|
||||
"server": "127.0.0.0.1",
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 8080
|
||||
},
|
||||
"fallback_for_alpn": {
|
||||
"http/1.1": {
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 8081
|
||||
}
|
||||
},
|
||||
"transport": {}
|
||||
}
|
||||
]
|
||||
@@ -52,7 +56,13 @@ TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
|
||||
|
||||
没有证据表明 GFW 基于 HTTP 响应检测并阻止木马服务器,并且在服务器上打开标准 http/s 端口是一个更大的特征。
|
||||
|
||||
备用服务器配置。默认禁用。
|
||||
回退服务器配置。如果 `fallback` 和 `fallback_for_alpn` 为空,则禁用回退。
|
||||
|
||||
#### fallback_for_alpn
|
||||
|
||||
为 ALPN 指定回退服务器配置。
|
||||
|
||||
如果不为空,ALPN 不在此列表中的 TLS 回退请求将被拒绝。
|
||||
|
||||
#### transport
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ TCP/IP 栈。
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含 LWIP 栈,请参阅 [安装](/zh/#installation)。
|
||||
默认安装不包含 LWIP 栈,请参阅 [安装](/zh/#_2)。
|
||||
|
||||
#### include_uid
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含被 Hysteria 依赖的 QUIC,参阅 [安装](/zh/#installation)。
|
||||
默认安装不包含被 Hysteria 依赖的 QUIC,参阅 [安装](/zh/#_2)。
|
||||
|
||||
### Hysteria 字段
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# Outbound
|
||||
|
||||
### Structure
|
||||
|
||||
```json
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
!!! info ""
|
||||
|
||||
默认安装不包含嵌入式 Tor, 参阅 [安装](/zh/#installation)。
|
||||
默认安装不包含嵌入式 Tor, 参阅 [安装](/zh/#_2)。
|
||||
|
||||
### Tor 字段
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含 WireGuard, 参阅 [安装](/zh/#installation)。
|
||||
默认安装不包含 WireGuard, 参阅 [安装](/zh/#_2)。
|
||||
|
||||
### WireGuard 字段
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# Route
|
||||
|
||||
### Structure
|
||||
|
||||
```json
|
||||
|
||||
@@ -41,10 +41,12 @@
|
||||
"cn"
|
||||
],
|
||||
"source_ip_cidr": [
|
||||
"10.0.0.0/24"
|
||||
"10.0.0.0/24",
|
||||
"192.168.0.1"
|
||||
],
|
||||
"ip_cidr": [
|
||||
"10.0.0.0/24"
|
||||
"10.0.0.0/24",
|
||||
"192.168.0.1"
|
||||
],
|
||||
"source_port": [
|
||||
12345
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含 ACME,参阅 [安装](/zh/#installation)。
|
||||
默认安装不包含 ACME,参阅 [安装](/zh/#_2)。
|
||||
|
||||
### 出站
|
||||
|
||||
|
||||
@@ -108,6 +108,10 @@ It needs to be consistent with the server.
|
||||
}
|
||||
```
|
||||
|
||||
!!! warning ""
|
||||
|
||||
QUIC is not included by default, see [Installation](/#installation).
|
||||
|
||||
!!! warning "Difference from v2ray-core"
|
||||
|
||||
No additional encryption support:
|
||||
|
||||
@@ -107,6 +107,10 @@ HTTP 请求的额外标头。
|
||||
}
|
||||
```
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含 QUIC, 参阅 [安装](/zh/#_2)。
|
||||
|
||||
!!! warning "与 v2ray-core 的区别"
|
||||
|
||||
没有额外的加密支持:
|
||||
@@ -116,7 +120,7 @@ HTTP 请求的额外标头。
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含 gRPC, 参阅 [安装](/zh/#installation)。
|
||||
默认安装不包含 gRPC, 参阅 [安装](/zh/#_2)。
|
||||
|
||||
```json
|
||||
{
|
||||
|
||||
50
docs/contributing/environment.md
Normal file
50
docs/contributing/environment.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# 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_insall
|
||||
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
|
||||
```
|
||||
17
docs/contributing/index.md
Normal file
17
docs/contributing/index.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# 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.
|
||||
|
||||
78
docs/contributing/sub-projects.md
Normal file
78
docs/contributing/sub-projects.md
Normal file
@@ -0,0 +1,78 @@
|
||||
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.
|
||||
|
||||
#### certmagic
|
||||
|
||||
Link: [GitHub repository](https://github.com/SagerNet/certmagic)
|
||||
|
||||
Fork of `caddyserver/certmagic`
|
||||
|
||||
Since upstream uses `miekg/dns` and we use `x/net/dnsmessage`, we need to replace its DNS part with our own
|
||||
implementation.
|
||||
|
||||
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.
|
||||
@@ -81,7 +81,7 @@
|
||||
| Feature | clash-premium |
|
||||
|-------------------------------------------|---------------|
|
||||
| Full IPv6 support | X |
|
||||
| Auto route on Linux/Windows/maxOS/Android | ✔ |
|
||||
| Auto route on Linux/Windows/macOS/Android | ✔ |
|
||||
| Embed windows driver | X |
|
||||
| Custom address/mtu | X |
|
||||
| Limit uid (Linux) in routing | X |
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
description: Welcome to the wiki page for the sing-box project.
|
||||
---
|
||||
|
||||
# Home
|
||||
|
||||
Welcome to the wiki page for the sing-box project.
|
||||
@@ -18,16 +22,16 @@ Install with options:
|
||||
go install -v -tags with_clash_api 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) and [Hysteria Outbound](./configuration/outbound/hysteria). |
|
||||
| `with_grpc` | Build with gRPC support, see [V2Ray Transport#gRPC](/configuration/shared/v2ray-transport#grpc). |
|
||||
| `with_wireguard` | Build with WireGuard support, see [WireGuard outbound](./configuration/outbound/wireguard). |
|
||||
| `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). |
|
||||
| `no_gvisor` | Build without gVisor Tun stack support, see [Tun inbound](./configuration/inbound/tun#stack). |
|
||||
| `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). |
|
||||
| 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 gRPC support, see [V2Ray Transport#gRPC](./configuration/shared/v2ray-transport#grpc). |
|
||||
| `with_wireguard` | Build with WireGuard support, see [WireGuard outbound](./configuration/outbound/wireguard). |
|
||||
| `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). |
|
||||
| `no_gvisor` | Build without gVisor Tun stack support, see [Tun inbound](./configuration/inbound/tun#stack). |
|
||||
| `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
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
description: 欢迎来到该 sing-box 项目的文档页。
|
||||
---
|
||||
|
||||
# 开始
|
||||
|
||||
欢迎来到该 sing-box 项目的文档页。
|
||||
@@ -18,16 +22,16 @@ go install -v github.com/sagernet/sing-box/cmd/sing-box@latest
|
||||
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)。 |
|
||||
| `with_grpc` | 启用 gRPC 支持,参阅 [V2Ray 传输层#gRPC](/configuration/shared/v2ray-transport#grpc)。 |
|
||||
| `with_wireguard` | 启用 WireGuard 支持,参阅 [WireGuard 出站](./configuration/outbound/wireguard)。 |
|
||||
| `with_acme` | 启用 ACME TLS 证书签发支持,参阅 [TLS](./configuration/shared/tls)。 |
|
||||
| `with_clash_api` | 启用 Clash api 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
|
||||
| `no_gvisor` | 禁用 gVisor Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
|
||||
| `with_embedded_tor` (需要 CGO) | 启用 嵌入式 Tor 支持,参阅 [Tor 出站](./configuration/outbound/tor)。 |
|
||||
| `with_lwip` (需要 CGO) | 启用 LWIP Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
|
||||
| 构建标志 | 描述 |
|
||||
|------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `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_wireguard` | 启用 WireGuard 支持,参阅 [WireGuard 出站](./configuration/outbound/wireguard)。 |
|
||||
| `with_acme` | 启用 ACME TLS 证书签发支持,参阅 [TLS](./configuration/shared/tls)。 |
|
||||
| `with_clash_api` | 启用 Clash api 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
|
||||
| `no_gvisor` | 禁用 gVisor Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
|
||||
| `with_embedded_tor` (需要 CGO) | 启用 嵌入式 Tor 支持,参阅 [Tor 出站](./configuration/outbound/tor)。 |
|
||||
| `with_lwip` (需要 CGO) | 启用 LWIP Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
|
||||
|
||||
二进制文件将被构建在 `$GOPATH/bin` 下。
|
||||
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
|
||||
"github.com/kardianos/service"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultDaemonName = "sing-box-daemon"
|
||||
DefaultDaemonPort = 9091
|
||||
)
|
||||
|
||||
var defaultDaemonOptions = Options{
|
||||
Listen: "127.0.0.1",
|
||||
ListenPort: DefaultDaemonPort,
|
||||
WorkingDirectory: workingDirectory(),
|
||||
}
|
||||
|
||||
func workingDirectory() string {
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
return filepath.Join("/usr/local/lib", DefaultDaemonName)
|
||||
default:
|
||||
configDir, err := os.UserConfigDir()
|
||||
if err == nil {
|
||||
return filepath.Join(configDir, DefaultDaemonName)
|
||||
} else {
|
||||
return DefaultDaemonName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const systemdScript = `[Unit]
|
||||
Description=sing-box service
|
||||
Documentation=https://sing-box.sagernet.org
|
||||
After=network.target nss-lookup.target
|
||||
|
||||
[Service]
|
||||
User=root
|
||||
ExecStart={{.Path|cmdEscape}}{{range .Arguments}} {{.|cmd}}{{end}}
|
||||
WorkingDirectory={{.WorkingDirectory|cmdEscape}}
|
||||
Restart=on-failure
|
||||
RestartSec=10s
|
||||
LimitNOFILE=infinity
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target`
|
||||
|
||||
type Daemon struct {
|
||||
service service.Service
|
||||
workingDirectory string
|
||||
executable string
|
||||
}
|
||||
|
||||
func New() (*Daemon, error) {
|
||||
daemonInterface := NewInterface(defaultDaemonOptions)
|
||||
executable := filepath.Join(defaultDaemonOptions.WorkingDirectory, "sing-box")
|
||||
if C.IsWindows {
|
||||
executable += ".exe"
|
||||
}
|
||||
daemonService, err := service.New(daemonInterface, &service.Config{
|
||||
Name: DefaultDaemonName,
|
||||
Description: "The universal proxy platform.",
|
||||
Arguments: []string{"daemon", "run"},
|
||||
Executable: executable,
|
||||
Option: service.KeyValue{
|
||||
"SystemdScript": systemdScript,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, E.New(strings.ToLower(err.Error()))
|
||||
}
|
||||
return &Daemon{
|
||||
service: daemonService,
|
||||
workingDirectory: defaultDaemonOptions.WorkingDirectory,
|
||||
executable: executable,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Daemon) Install() error {
|
||||
_, err := d.service.Status()
|
||||
if err != service.ErrNotInstalled {
|
||||
d.service.Stop()
|
||||
err = d.service.Uninstall()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
executablePath, err := os.Executable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !rw.FileExists(d.workingDirectory) {
|
||||
err = os.MkdirAll(d.workingDirectory, 0o755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
outputFile, err := os.OpenFile(d.executable, os.O_CREATE|os.O_WRONLY, 0o755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
inputFile, err := os.Open(executablePath)
|
||||
if err != nil {
|
||||
outputFile.Close()
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(outputFile, inputFile)
|
||||
inputFile.Close()
|
||||
outputFile.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.service.Install()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return d.service.Start()
|
||||
}
|
||||
|
||||
func (d *Daemon) Uninstall() error {
|
||||
_, err := d.service.Status()
|
||||
if err != service.ErrNotInstalled {
|
||||
d.service.Stop()
|
||||
err = d.service.Uninstall()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return os.RemoveAll(d.workingDirectory)
|
||||
}
|
||||
|
||||
func (d *Daemon) Run() error {
|
||||
d.chdir()
|
||||
return d.service.Run()
|
||||
}
|
||||
|
||||
func (d *Daemon) chdir() error {
|
||||
executable, err := os.Executable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Chdir(filepath.Dir(executable))
|
||||
}
|
||||
|
||||
func (d *Daemon) Start() error {
|
||||
return d.service.Start()
|
||||
}
|
||||
|
||||
func (d *Daemon) Stop() error {
|
||||
return d.service.Stop()
|
||||
}
|
||||
|
||||
func (d *Daemon) Restart() error {
|
||||
return d.service.Restart()
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
type Instance struct {
|
||||
access sync.Mutex
|
||||
boxInstance *box.Box
|
||||
boxCancel context.CancelFunc
|
||||
}
|
||||
|
||||
func (i *Instance) Running() bool {
|
||||
i.access.Lock()
|
||||
defer i.access.Unlock()
|
||||
return i.boxInstance != nil
|
||||
}
|
||||
|
||||
func (i *Instance) Start(options option.Options) error {
|
||||
i.access.Lock()
|
||||
defer i.access.Unlock()
|
||||
if i.boxInstance != nil {
|
||||
i.boxCancel()
|
||||
i.boxInstance.Close()
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
instance, err := box.New(ctx, options)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return err
|
||||
}
|
||||
err = instance.Start()
|
||||
if err != nil {
|
||||
cancel()
|
||||
return err
|
||||
}
|
||||
i.boxInstance = instance
|
||||
i.boxCancel = cancel
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Instance) Close() error {
|
||||
i.access.Lock()
|
||||
defer i.access.Unlock()
|
||||
if i.boxInstance == nil {
|
||||
return os.ErrClosed
|
||||
}
|
||||
i.boxCancel()
|
||||
err := i.boxInstance.Close()
|
||||
i.boxInstance = nil
|
||||
i.boxCancel = nil
|
||||
return err
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package daemon
|
||||
|
||||
import "github.com/kardianos/service"
|
||||
|
||||
type Interface struct {
|
||||
server *Server
|
||||
}
|
||||
|
||||
func NewInterface(options Options) *Interface {
|
||||
return &Interface{NewServer(options)}
|
||||
}
|
||||
|
||||
func (d *Interface) Start(_ service.Service) error {
|
||||
return d.server.Start()
|
||||
}
|
||||
|
||||
func (d *Interface) Stop(_ service.Service) error {
|
||||
d.server.Close()
|
||||
return nil
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/common/json"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/cors"
|
||||
"github.com/go-chi/render"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Listen string `json:"listen"`
|
||||
ListenPort uint16 `json:"listen_port"`
|
||||
Secret string `json:"secret"`
|
||||
WorkingDirectory string `json:"working_directory"`
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
options Options
|
||||
httpServer *http.Server
|
||||
instance Instance
|
||||
}
|
||||
|
||||
func NewServer(options Options) *Server {
|
||||
return &Server{
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Start() error {
|
||||
tcpConn, err := net.Listen("tcp", net.JoinHostPort(s.options.Listen, F.ToString(s.options.ListenPort)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
router := chi.NewRouter()
|
||||
router.Use(cors.New(cors.Options{
|
||||
AllowedOrigins: []string{"*"},
|
||||
AllowedMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE"},
|
||||
AllowedHeaders: []string{"Content-Type", "Authorization"},
|
||||
MaxAge: 300,
|
||||
}).Handler)
|
||||
if s.options.Secret != "" {
|
||||
router.Use(s.authentication)
|
||||
}
|
||||
router.Get("/ping", s.ping)
|
||||
router.Get("/status", s.status)
|
||||
router.Post("/run", s.run)
|
||||
router.Get("/stop", s.stop)
|
||||
router.Route("/debug/pprof", func(r chi.Router) {
|
||||
r.HandleFunc("/", pprof.Index)
|
||||
r.HandleFunc("/cmdline", pprof.Cmdline)
|
||||
r.HandleFunc("/profile", pprof.Profile)
|
||||
r.HandleFunc("/symbol", pprof.Symbol)
|
||||
r.HandleFunc("/trace", pprof.Trace)
|
||||
})
|
||||
httpServer := &http.Server{
|
||||
Handler: router,
|
||||
}
|
||||
go httpServer.Serve(tcpConn)
|
||||
s.httpServer = httpServer
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) authentication(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
if websocket.IsWebSocketUpgrade(request) && request.URL.Query().Get("token") != "" {
|
||||
token := request.URL.Query().Get("token")
|
||||
if token != s.options.Secret {
|
||||
render.Status(request, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(writer, request)
|
||||
return
|
||||
}
|
||||
header := request.Header.Get("Authorization")
|
||||
bearer, token, found := strings.Cut(header, " ")
|
||||
hasInvalidHeader := bearer != "Bearer"
|
||||
hasInvalidSecret := !found || token != s.options.Secret
|
||||
if hasInvalidHeader || hasInvalidSecret {
|
||||
render.Status(request, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(writer, request)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) Close() error {
|
||||
return common.Close(
|
||||
common.PtrOrNil(s.httpServer),
|
||||
&s.instance,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *Server) ping(writer http.ResponseWriter, request *http.Request) {
|
||||
render.PlainText(writer, request, "pong")
|
||||
}
|
||||
|
||||
type StatusResponse struct {
|
||||
Running bool `json:"running"`
|
||||
}
|
||||
|
||||
func (s *Server) status(writer http.ResponseWriter, request *http.Request) {
|
||||
render.JSON(writer, request, StatusResponse{
|
||||
Running: s.instance.Running(),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) run(writer http.ResponseWriter, request *http.Request) {
|
||||
err := s.run0(request)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
render.Status(request, http.StatusBadRequest)
|
||||
render.PlainText(writer, request, err.Error())
|
||||
return
|
||||
}
|
||||
writer.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (s *Server) run0(request *http.Request) error {
|
||||
configContent, err := io.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
return E.Cause(err, "read config")
|
||||
}
|
||||
var options option.Options
|
||||
err = json.Unmarshal(configContent, &options)
|
||||
if err != nil {
|
||||
return E.Cause(err, "decode config")
|
||||
}
|
||||
return s.instance.Start(options)
|
||||
}
|
||||
|
||||
func (s *Server) stop(writer http.ResponseWriter, request *http.Request) {
|
||||
s.instance.Close()
|
||||
writer.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
11
go.mod
11
go.mod
@@ -14,7 +14,6 @@ require (
|
||||
github.com/gofrs/uuid v4.2.0+incompatible
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/hashicorp/yamux v0.1.1
|
||||
github.com/kardianos/service v1.2.1
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
github.com/mholt/acmez v1.0.4
|
||||
github.com/oschwald/maxminddb-golang v1.10.0
|
||||
@@ -22,15 +21,16 @@ require (
|
||||
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a
|
||||
github.com/sagernet/netlink v0.0.0-20220820041223-3cd8365d17ac
|
||||
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb
|
||||
github.com/sagernet/sing v0.0.0-20220824062950-7bfd820739a8
|
||||
github.com/sagernet/sing v0.0.0-20220825093630-185d87918290
|
||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
|
||||
github.com/sagernet/sing-tun v0.0.0-20220824105617-e5c59fc756a6
|
||||
github.com/sagernet/sing-tun v0.0.0-20220826042514-409136e64474
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220811135656-4f3f07acf9c4
|
||||
github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939
|
||||
github.com/spf13/cobra v1.5.0
|
||||
github.com/stretchr/testify v1.8.0
|
||||
go.uber.org/atomic v1.10.0
|
||||
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8
|
||||
golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c
|
||||
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6
|
||||
@@ -63,11 +63,10 @@ require (
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.22.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||
golang.org/x/tools v0.1.10 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
|
||||
22
go.sum
22
go.sum
@@ -88,8 +88,6 @@ github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbg
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/kardianos/service v1.2.1 h1:AYndMsehS+ywIS6RB9KOlcXzteWUzxgMgBymJD7+BYk=
|
||||
github.com/kardianos/service v1.2.1/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0=
|
||||
github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
@@ -144,14 +142,14 @@ github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb h1:wc0yQ+SBn4TaTY
|
||||
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4=
|
||||
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/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.0.0-20220824062950-7bfd820739a8 h1:kHsinrGrMjEh5KUXC/MPCS+Uy3Z3XO/cMhC8xJtABE8=
|
||||
github.com/sagernet/sing v0.0.0-20220824062950-7bfd820739a8/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
|
||||
github.com/sagernet/sing v0.0.0-20220825093630-185d87918290 h1:OAt6dFNrGOpgaPgM3uvAdQE0NkGC7AAygqpo8MwryY8=
|
||||
github.com/sagernet/sing v0.0.0-20220825093630-185d87918290/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFxUX+xJCWMPPFZCn2GC/uLrBjTSr1vHY=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM=
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220824105617-e5c59fc756a6 h1:C0uNMDrjYribl4Pu41Au9UeQROIOeMWaDd7eSUIQ9gQ=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220824105617-e5c59fc756a6/go.mod h1:zMKRFCEoO6Jp5Yxb2NUTqc+SvAtNVAmzfwArAheJy5g=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220826042514-409136e64474 h1:dy9dLOSTDtig/s5se7cRqIypUlqtcp4+Zw0+XMpORPE=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220826042514-409136e64474/go.mod h1:zMKRFCEoO6Jp5Yxb2NUTqc+SvAtNVAmzfwArAheJy5g=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220811135656-4f3f07acf9c4 h1:2hLETh97+S4WnfMR27XyC7QVU1SH7FTNoCznP229YJU=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220811135656-4f3f07acf9c4/go.mod h1:82O6gzbxLha/W/jxSVQbsqf2lVdRTjMIgyLug0lpJps=
|
||||
github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939 h1:pB1Dh1NbwVrLhQhotr4O4Hs3yhiBzmg3AvnUyYjL4x4=
|
||||
@@ -183,6 +181,8 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
go.uber.org/zap v1.22.0 h1:Zcye5DUgBloQ9BaT4qc9BnjOFog5TvBSAGkJ3Nf70c0=
|
||||
go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U=
|
||||
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d h1:ggxwEf5eu0l8v+87VhX1czFh8zJul3hK16Gmruxn7hw=
|
||||
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@@ -201,8 +201,8 @@ golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPI
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -241,7 +241,6 @@ golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -277,12 +276,11 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f h1:OKYpQQVE3DKSc3r3zHVzq46vq5YH7x8xpR3/k9ixmUg=
|
||||
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
|
||||
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=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
|
||||
@@ -187,7 +187,6 @@ func (a *myInboundAdapter) createMetadata(conn net.Conn, metadata adapter.Inboun
|
||||
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
||||
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
||||
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
||||
metadata.Network = N.NetworkTCP
|
||||
if !metadata.Source.IsValid() {
|
||||
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr())
|
||||
}
|
||||
@@ -242,7 +241,6 @@ func (a *myInboundAdapter) loopUDPIn() {
|
||||
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
||||
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
||||
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
||||
metadata.Network = N.NetworkUDP
|
||||
metadata.Source = M.SocksaddrFromNetIP(addr)
|
||||
metadata.OriginDestination = a.udpAddr
|
||||
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
|
||||
@@ -275,7 +273,6 @@ func (a *myInboundAdapter) loopUDPOOBIn() {
|
||||
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
||||
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
||||
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
||||
metadata.Network = N.NetworkUDP
|
||||
metadata.Source = M.SocksaddrFromNetIP(addr)
|
||||
metadata.OriginDestination = a.udpAddr
|
||||
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
|
||||
@@ -302,7 +299,6 @@ func (a *myInboundAdapter) loopUDPInThreadSafe() {
|
||||
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
||||
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
||||
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
||||
metadata.Network = N.NetworkUDP
|
||||
metadata.Source = M.SocksaddrFromNetIP(addr)
|
||||
metadata.OriginDestination = a.udpAddr
|
||||
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
|
||||
@@ -331,7 +327,6 @@ func (a *myInboundAdapter) loopUDPOOBInThreadSafe() {
|
||||
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
||||
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
||||
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
||||
metadata.Network = N.NetworkUDP
|
||||
metadata.Source = M.SocksaddrFromNetIP(addr)
|
||||
metadata.OriginDestination = a.udpAddr
|
||||
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
|
||||
|
||||
@@ -267,7 +267,6 @@ func (h *Hysteria) acceptStream(ctx context.Context, conn quic.Connection, strea
|
||||
metadata.Destination = M.ParseSocksaddrHostPort(request.Host, request.Port)
|
||||
if !request.UDP {
|
||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
metadata.Network = N.NetworkTCP
|
||||
return h.router.RouteConnection(ctx, hysteria.NewConn(stream, metadata.Destination), metadata)
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
@@ -278,7 +277,6 @@ func (h *Hysteria) acceptStream(ctx context.Context, conn quic.Connection, strea
|
||||
h.udpSessions[id] = nCh
|
||||
h.udpSessionId += 1
|
||||
h.udpAccess.Unlock()
|
||||
metadata.Network = N.NetworkUDP
|
||||
packetConn := hysteria.NewPacketConn(conn, stream, id, metadata.Destination, nCh, common.Closer(func() error {
|
||||
h.udpAccess.Lock()
|
||||
if ch, ok := h.udpSessions[id]; ok {
|
||||
|
||||
@@ -24,11 +24,12 @@ var _ adapter.Inbound = (*Trojan)(nil)
|
||||
|
||||
type Trojan struct {
|
||||
myInboundAdapter
|
||||
service *trojan.Service[int]
|
||||
users []option.TrojanUser
|
||||
tlsConfig *TLSConfig
|
||||
fallbackAddr M.Socksaddr
|
||||
transport adapter.V2RayServerTransport
|
||||
service *trojan.Service[int]
|
||||
users []option.TrojanUser
|
||||
tlsConfig *TLSConfig
|
||||
fallbackAddr M.Socksaddr
|
||||
fallbackAddrTLSNextProto map[string]M.Socksaddr
|
||||
transport adapter.V2RayServerTransport
|
||||
}
|
||||
|
||||
func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TrojanInboundOptions) (*Trojan, error) {
|
||||
@@ -44,9 +45,35 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
|
||||
},
|
||||
users: options.Users,
|
||||
}
|
||||
if options.TLS != nil {
|
||||
tlsConfig, err := NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inbound.tlsConfig = tlsConfig
|
||||
}
|
||||
var fallbackHandler N.TCPConnectionHandler
|
||||
if options.Fallback != nil && options.Fallback.Server != "" {
|
||||
inbound.fallbackAddr = options.Fallback.Build()
|
||||
if options.Fallback != nil && options.Fallback.Server != "" || len(options.FallbackForALPN) > 0 {
|
||||
if options.Fallback != nil && options.Fallback.Server != "" {
|
||||
inbound.fallbackAddr = options.Fallback.Build()
|
||||
if !inbound.fallbackAddr.IsValid() {
|
||||
return nil, E.New("invalid fallback address: ", inbound.fallbackAddr)
|
||||
}
|
||||
}
|
||||
if len(options.FallbackForALPN) > 0 {
|
||||
if inbound.tlsConfig == nil {
|
||||
return nil, E.New("fallback for ALPN is not supported without TLS")
|
||||
}
|
||||
fallbackAddrNextProto := make(map[string]M.Socksaddr)
|
||||
for nextProto, destination := range options.FallbackForALPN {
|
||||
fallbackAddr := destination.Build()
|
||||
if !fallbackAddr.IsValid() {
|
||||
return nil, E.New("invalid fallback address for ALPN ", nextProto, ": ", fallbackAddr)
|
||||
}
|
||||
fallbackAddrNextProto[nextProto] = fallbackAddr
|
||||
}
|
||||
inbound.fallbackAddrTLSNextProto = fallbackAddrNextProto
|
||||
}
|
||||
fallbackHandler = adapter.NewUpstreamContextHandler(inbound.fallbackConnection, nil, nil)
|
||||
}
|
||||
service := trojan.NewService[int](adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound), fallbackHandler)
|
||||
@@ -58,13 +85,6 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if options.TLS != nil {
|
||||
tlsConfig, err := NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inbound.tlsConfig = tlsConfig
|
||||
}
|
||||
if options.Transport != nil {
|
||||
var tlsConfig *tls.Config
|
||||
if inbound.tlsConfig != nil {
|
||||
@@ -153,8 +173,22 @@ func (h *Trojan) newConnection(ctx context.Context, conn net.Conn, metadata adap
|
||||
}
|
||||
|
||||
func (h *Trojan) fallbackConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
h.logger.InfoContext(ctx, "fallback connection to ", h.fallbackAddr)
|
||||
metadata.Destination = h.fallbackAddr
|
||||
var fallbackAddr M.Socksaddr
|
||||
if len(h.fallbackAddrTLSNextProto) > 0 {
|
||||
if tlsConn, loaded := common.Cast[*tls.Conn](conn); loaded {
|
||||
connectionState := tlsConn.ConnectionState()
|
||||
if connectionState.NegotiatedProtocol != "" {
|
||||
if fallbackAddr, loaded = h.fallbackAddrTLSNextProto[connectionState.NegotiatedProtocol]; !loaded {
|
||||
return E.New("fallback disabled for ALPN: ", connectionState.NegotiatedProtocol)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !fallbackAddr.IsValid() {
|
||||
fallbackAddr = h.fallbackAddr
|
||||
}
|
||||
h.logger.InfoContext(ctx, "fallback connection to ", fallbackAddr)
|
||||
metadata.Destination = fallbackAddr
|
||||
return h.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
|
||||
@@ -165,7 +165,6 @@ func (t *Tun) NewConnection(ctx context.Context, conn net.Conn, upstreamMetadata
|
||||
var metadata adapter.InboundContext
|
||||
metadata.Inbound = t.tag
|
||||
metadata.InboundType = C.TypeTun
|
||||
metadata.Network = N.NetworkTCP
|
||||
metadata.Source = upstreamMetadata.Source
|
||||
metadata.Destination = upstreamMetadata.Destination
|
||||
metadata.SniffEnabled = t.inboundOptions.SniffEnabled
|
||||
@@ -188,7 +187,6 @@ func (t *Tun) NewPacketConnection(ctx context.Context, conn N.PacketConn, upstre
|
||||
var metadata adapter.InboundContext
|
||||
metadata.Inbound = t.tag
|
||||
metadata.InboundType = C.TypeTun
|
||||
metadata.Network = N.NetworkUDP
|
||||
metadata.Source = upstreamMetadata.Source
|
||||
metadata.Destination = upstreamMetadata.Destination
|
||||
metadata.SniffEnabled = t.inboundOptions.SniffEnabled
|
||||
|
||||
@@ -49,7 +49,7 @@ nav:
|
||||
- Route Rule: configuration/route/rule.md
|
||||
- Protocol Sniff: configuration/route/sniff.md
|
||||
- Experimental:
|
||||
- configuration/experimental/index.md
|
||||
- configuration/experimental/index.md
|
||||
- Shared:
|
||||
- TLS: configuration/shared/tls.md
|
||||
- Multiplex: configuration/shared/multiplex.md
|
||||
@@ -93,6 +93,11 @@ nav:
|
||||
- Shadowsocks Client: examples/ss-client.md
|
||||
- Shadowsocks Tun: examples/ss-tun.md
|
||||
- DNS Hijack: examples/dns-hijack.md
|
||||
- Contributing:
|
||||
- contributing/index.md
|
||||
- Developing:
|
||||
- Environment: contributing/environment.md
|
||||
- Sub projects: contributing/sub-projects.md
|
||||
markdown_extensions:
|
||||
- pymdownx.inlinehilite
|
||||
- pymdownx.snippets
|
||||
|
||||
@@ -100,14 +100,14 @@ func (h *Outbound) UnmarshalJSON(bytes []byte) error {
|
||||
}
|
||||
|
||||
type DialerOptions struct {
|
||||
Detour string `json:"detour,omitempty"`
|
||||
BindInterface string `json:"bind_interface,omitempty"`
|
||||
BindAddress ListenAddress `json:"bind_address,omitempty"`
|
||||
ProtectPath string `json:"protect_path,omitempty"`
|
||||
RoutingMark int `json:"routing_mark,omitempty"`
|
||||
ReuseAddr bool `json:"reuse_addr,omitempty"`
|
||||
ConnectTimeout Duration `json:"connect_timeout,omitempty"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
BindInterface string `json:"bind_interface,omitempty"`
|
||||
BindAddress *ListenAddress `json:"bind_address,omitempty"`
|
||||
ProtectPath string `json:"protect_path,omitempty"`
|
||||
RoutingMark int `json:"routing_mark,omitempty"`
|
||||
ReuseAddr bool `json:"reuse_addr,omitempty"`
|
||||
ConnectTimeout Duration `json:"connect_timeout,omitempty"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
}
|
||||
|
||||
type OutboundDialerOptions struct {
|
||||
|
||||
@@ -2,10 +2,11 @@ package option
|
||||
|
||||
type TrojanInboundOptions struct {
|
||||
ListenOptions
|
||||
Users []TrojanUser `json:"users,omitempty"`
|
||||
TLS *InboundTLSOptions `json:"tls,omitempty"`
|
||||
Fallback *ServerOptions `json:"fallback,omitempty"`
|
||||
Transport *V2RayTransportOptions `json:"transport,omitempty"`
|
||||
Users []TrojanUser `json:"users,omitempty"`
|
||||
TLS *InboundTLSOptions `json:"tls,omitempty"`
|
||||
Fallback *ServerOptions `json:"fallback,omitempty"`
|
||||
FallbackForALPN map[string]*ServerOptions `json:"fallback_for_alpn,omitempty"`
|
||||
Transport *V2RayTransportOptions `json:"transport,omitempty"`
|
||||
}
|
||||
|
||||
type TrojanUser struct {
|
||||
|
||||
@@ -16,7 +16,7 @@ type ListenAddress netip.Addr
|
||||
func (a ListenAddress) MarshalJSON() ([]byte, error) {
|
||||
addr := netip.Addr(a)
|
||||
if !addr.IsValid() {
|
||||
return json.Marshal("")
|
||||
return nil, nil
|
||||
}
|
||||
return json.Marshal(addr.String())
|
||||
}
|
||||
@@ -35,6 +35,10 @@ func (a *ListenAddress) UnmarshalJSON(content []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a ListenAddress) Build() netip.Addr {
|
||||
return (netip.Addr)(a)
|
||||
}
|
||||
|
||||
type NetworkList string
|
||||
|
||||
func (v *NetworkList) UnmarshalJSON(content []byte) error {
|
||||
|
||||
@@ -3,13 +3,13 @@ package outbound
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/canceler"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
@@ -47,53 +47,60 @@ func (d *DNS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.Pa
|
||||
func (d *DNS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
defer conn.Close()
|
||||
ctx = adapter.WithContext(ctx, &metadata)
|
||||
_buffer := buf.StackNewSize(1024)
|
||||
for {
|
||||
err := d.handleConnection(ctx, conn, metadata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNS) handleConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
var queryLength uint16
|
||||
err := binary.Read(conn, binary.BigEndian, &queryLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if queryLength == 0 {
|
||||
return dns.RCodeFormatError
|
||||
}
|
||||
_buffer := buf.StackNewSize(int(queryLength))
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
defer buffer.Release()
|
||||
for {
|
||||
var queryLength uint16
|
||||
err := binary.Read(conn, binary.BigEndian, &queryLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if queryLength > 1024 {
|
||||
return io.ErrShortBuffer
|
||||
}
|
||||
buffer.FullReset()
|
||||
_, err = buffer.ReadFullFrom(conn, int(queryLength))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var message dnsmessage.Message
|
||||
err = message.Unpack(buffer.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(message.Questions) > 0 {
|
||||
question := message.Questions[0]
|
||||
metadata.Domain = string(question.Name.Data[:question.Name.Length-1])
|
||||
}
|
||||
go func() error {
|
||||
response, err := d.router.Exchange(ctx, &message)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_responseBuffer := buf.StackNewPacket()
|
||||
defer common.KeepAlive(_responseBuffer)
|
||||
responseBuffer := common.Dup(_responseBuffer)
|
||||
defer responseBuffer.Release()
|
||||
responseBuffer.Resize(2, 0)
|
||||
n, err := response.AppendPack(responseBuffer.Index(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
responseBuffer.Truncate(len(n))
|
||||
binary.BigEndian.PutUint16(responseBuffer.ExtendHeader(2), uint16(len(n)))
|
||||
_, err = conn.Write(responseBuffer.Bytes())
|
||||
return err
|
||||
}()
|
||||
_, err = buffer.ReadFullFrom(conn, int(queryLength))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var message dnsmessage.Message
|
||||
err = message.Unpack(buffer.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(message.Questions) > 0 {
|
||||
question := message.Questions[0]
|
||||
metadata.Domain = string(question.Name.Data[:question.Name.Length-1])
|
||||
}
|
||||
go func() error {
|
||||
response, err := d.router.Exchange(ctx, &message)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_responseBuffer := buf.StackNewPacket()
|
||||
defer common.KeepAlive(_responseBuffer)
|
||||
responseBuffer := common.Dup(_responseBuffer)
|
||||
defer responseBuffer.Release()
|
||||
responseBuffer.Resize(2, 0)
|
||||
n, err := response.AppendPack(responseBuffer.Index(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
responseBuffer.Truncate(len(n))
|
||||
binary.BigEndian.PutUint16(responseBuffer.ExtendHeader(2), uint16(len(n)))
|
||||
_, err = conn.Write(responseBuffer.Bytes())
|
||||
return err
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
@@ -103,7 +110,7 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada
|
||||
var group task.Group
|
||||
group.Append0(func(ctx context.Context) error {
|
||||
defer cancel()
|
||||
_buffer := buf.StackNewSize(1024)
|
||||
_buffer := buf.StackNewSize(dns.FixedPacketSize)
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
defer buffer.Release()
|
||||
|
||||
@@ -32,17 +32,6 @@ type VMess struct {
|
||||
}
|
||||
|
||||
func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VMessOutboundOptions) (*VMess, error) {
|
||||
var clientOptions []vmess.ClientOption
|
||||
if options.GlobalPadding {
|
||||
clientOptions = append(clientOptions, vmess.ClientWithGlobalPadding())
|
||||
}
|
||||
if options.AuthenticatedLength {
|
||||
clientOptions = append(clientOptions, vmess.ClientWithAuthenticatedLength())
|
||||
}
|
||||
client, err := vmess.NewClient(options.UUID, options.Security, options.AlterId, clientOptions...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound := &VMess{
|
||||
myOutboundAdapter: myOutboundAdapter{
|
||||
protocol: C.TypeVMess,
|
||||
@@ -52,9 +41,9 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
tag: tag,
|
||||
},
|
||||
dialer: dialer.NewOutbound(router, options.OutboundDialerOptions),
|
||||
client: client,
|
||||
serverAddr: options.ServerOptions.Build(),
|
||||
}
|
||||
var err error
|
||||
if options.TLS != nil {
|
||||
outbound.tlsConfig, err = dialer.TLSConfig(options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
@@ -71,6 +60,25 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var clientOptions []vmess.ClientOption
|
||||
if options.GlobalPadding {
|
||||
clientOptions = append(clientOptions, vmess.ClientWithGlobalPadding())
|
||||
}
|
||||
if options.AuthenticatedLength {
|
||||
clientOptions = append(clientOptions, vmess.ClientWithAuthenticatedLength())
|
||||
}
|
||||
security := options.Security
|
||||
if security == "" {
|
||||
security = "auto"
|
||||
}
|
||||
if security == "auto" && outbound.tlsConfig != nil {
|
||||
security = "zero"
|
||||
}
|
||||
client, err := vmess.NewClient(options.UUID, security, options.AlterId, clientOptions...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.client = client
|
||||
return outbound, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -498,13 +498,13 @@ func (r *Router) DefaultOutbound(network string) adapter.Outbound {
|
||||
}
|
||||
|
||||
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
metadata.Network = N.NetworkTCP
|
||||
switch metadata.Destination.Fqdn {
|
||||
case mux.Destination.Fqdn:
|
||||
r.logger.InfoContext(ctx, "inbound multiplex connection")
|
||||
return mux.NewConnection(ctx, r, r, r.logger, conn, metadata)
|
||||
case uot.UOTMagicAddress:
|
||||
r.logger.InfoContext(ctx, "inbound UoT connection")
|
||||
metadata.Network = N.NetworkUDP
|
||||
metadata.Destination = M.Socksaddr{}
|
||||
return r.RoutePacketConnection(ctx, uot.NewClientConn(conn), metadata)
|
||||
}
|
||||
@@ -552,6 +552,7 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
}
|
||||
|
||||
func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
metadata.Network = N.NetworkUDP
|
||||
if metadata.SniffEnabled {
|
||||
buffer := buf.NewPacket()
|
||||
buffer.FullReset()
|
||||
|
||||
@@ -6,51 +6,67 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
|
||||
"go4.org/netipx"
|
||||
)
|
||||
|
||||
var _ RuleItem = (*IPCIDRItem)(nil)
|
||||
|
||||
type IPCIDRItem struct {
|
||||
prefixes []netip.Prefix
|
||||
isSource bool
|
||||
ipSet *netipx.IPSet
|
||||
isSource bool
|
||||
description string
|
||||
}
|
||||
|
||||
func NewIPCIDRItem(isSource bool, prefixStrings []string) (*IPCIDRItem, error) {
|
||||
prefixes := make([]netip.Prefix, 0, len(prefixStrings))
|
||||
var builder netipx.IPSetBuilder
|
||||
for i, prefixString := range prefixStrings {
|
||||
prefix, err := netip.ParsePrefix(prefixString)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse prefix [", i, "]")
|
||||
if err == nil {
|
||||
builder.AddPrefix(prefix)
|
||||
continue
|
||||
}
|
||||
prefixes = append(prefixes, prefix)
|
||||
addr, addrErr := netip.ParseAddr(prefixString)
|
||||
if addrErr == nil {
|
||||
builder.Add(addr)
|
||||
continue
|
||||
}
|
||||
return nil, E.Cause(err, "parse ip_cidr [", i, "]")
|
||||
}
|
||||
var description string
|
||||
if isSource {
|
||||
description = "source_ipcidr="
|
||||
} else {
|
||||
description = "ipcidr="
|
||||
}
|
||||
if dLen := len(prefixStrings); dLen == 1 {
|
||||
description += prefixStrings[0]
|
||||
} else if dLen > 3 {
|
||||
description += "[" + strings.Join(prefixStrings[:3], " ") + "...]"
|
||||
} else {
|
||||
description += "[" + strings.Join(prefixStrings, " ") + "]"
|
||||
}
|
||||
ipSet, err := builder.IPSet()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &IPCIDRItem{
|
||||
prefixes: prefixes,
|
||||
isSource: isSource,
|
||||
ipSet: ipSet,
|
||||
isSource: isSource,
|
||||
description: description,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *IPCIDRItem) Match(metadata *adapter.InboundContext) bool {
|
||||
if r.isSource {
|
||||
for _, prefix := range r.prefixes {
|
||||
if prefix.Contains(metadata.Source.Addr) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return r.ipSet.Contains(metadata.Source.Addr)
|
||||
} else {
|
||||
if metadata.Destination.IsIP() {
|
||||
for _, prefix := range r.prefixes {
|
||||
if prefix.Contains(metadata.Destination.Addr) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return r.ipSet.Contains(metadata.Destination.Addr)
|
||||
} else {
|
||||
for _, address := range metadata.DestinationAddresses {
|
||||
for _, prefix := range r.prefixes {
|
||||
if prefix.Contains(address) {
|
||||
return true
|
||||
}
|
||||
if r.ipSet.Contains(address) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,17 +75,5 @@ func (r *IPCIDRItem) Match(metadata *adapter.InboundContext) bool {
|
||||
}
|
||||
|
||||
func (r *IPCIDRItem) String() string {
|
||||
var description string
|
||||
if r.isSource {
|
||||
description = "source_ipcidr="
|
||||
} else {
|
||||
description = "ipcidr="
|
||||
}
|
||||
pLen := len(r.prefixes)
|
||||
if pLen == 1 {
|
||||
description += r.prefixes[0].String()
|
||||
} else {
|
||||
description += "[" + strings.Join(F.MapToString(r.prefixes), " ") + "]"
|
||||
}
|
||||
return description
|
||||
return r.description
|
||||
}
|
||||
|
||||
@@ -74,13 +74,13 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
||||
|
||||
func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
if !c.http2 {
|
||||
return c.dialHTTP()
|
||||
return c.dialHTTP(ctx)
|
||||
} else {
|
||||
return c.dialHTTP2()
|
||||
return c.dialHTTP2(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) dialHTTP() (net.Conn, error) {
|
||||
func (c *Client) dialHTTP(ctx context.Context) (net.Conn, error) {
|
||||
conn, err := c.dialer.DialContext(c.ctx, N.NetworkTCP, c.serverAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -92,6 +92,7 @@ func (c *Client) dialHTTP() (net.Conn, error) {
|
||||
Proto: "HTTP/1.1",
|
||||
Header: c.headers.Clone(),
|
||||
}
|
||||
request = request.WithContext(ctx)
|
||||
switch hostLen := len(c.host); hostLen {
|
||||
case 0:
|
||||
case 1:
|
||||
@@ -114,7 +115,7 @@ func (c *Client) dialHTTP() (net.Conn, error) {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *Client) dialHTTP2() (net.Conn, error) {
|
||||
func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) {
|
||||
pipeInReader, pipeInWriter := io.Pipe()
|
||||
request := &http.Request{
|
||||
Method: c.method,
|
||||
@@ -124,6 +125,7 @@ func (c *Client) dialHTTP2() (net.Conn, error) {
|
||||
Proto: "HTTP/2",
|
||||
Header: c.headers.Clone(),
|
||||
}
|
||||
request = request.WithContext(ctx)
|
||||
switch hostLen := len(c.host); hostLen {
|
||||
case 0:
|
||||
case 1:
|
||||
|
||||
@@ -90,9 +90,8 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
}
|
||||
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
if f, ok := writer.(http.Flusher); ok {
|
||||
f.Flush()
|
||||
}
|
||||
writer.(http.Flusher).Flush()
|
||||
|
||||
var metadata M.Metadata
|
||||
metadata.Source = sHttp.SourceAddress(request)
|
||||
if h, ok := writer.(http.Hijacker); ok {
|
||||
|
||||
@@ -70,7 +70,7 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
}
|
||||
return nil, wrapDialError(response, err)
|
||||
} else {
|
||||
return &EarlyWebsocketConn{Client: c, create: make(chan struct{})}, nil
|
||||
return &EarlyWebsocketConn{Client: c, ctx: ctx, create: make(chan struct{})}, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package v2raywebsocket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"net"
|
||||
@@ -68,6 +69,7 @@ func (c *WebsocketConn) SetDeadline(t time.Time) error {
|
||||
|
||||
type EarlyWebsocketConn struct {
|
||||
*Client
|
||||
ctx context.Context
|
||||
conn *WebsocketConn
|
||||
create chan struct{}
|
||||
}
|
||||
@@ -98,14 +100,14 @@ func (c *EarlyWebsocketConn) Write(b []byte) (n int, err error) {
|
||||
if len(earlyData) > 0 {
|
||||
earlyDataString := base64.RawURLEncoding.EncodeToString(earlyData)
|
||||
if c.earlyDataHeaderName == "" {
|
||||
conn, response, err = c.dialer.Dial(c.uri+earlyDataString, c.headers)
|
||||
conn, response, err = c.dialer.DialContext(c.ctx, c.uri+earlyDataString, c.headers)
|
||||
} else {
|
||||
headers := c.headers.Clone()
|
||||
headers.Set(c.earlyDataHeaderName, earlyDataString)
|
||||
conn, response, err = c.dialer.Dial(c.uri, headers)
|
||||
conn, response, err = c.dialer.DialContext(c.ctx, c.uri, headers)
|
||||
}
|
||||
} else {
|
||||
conn, response, err = c.dialer.Dial(c.uri, c.headers)
|
||||
conn, response, err = c.dialer.DialContext(c.ctx, c.uri, c.headers)
|
||||
}
|
||||
if err != nil {
|
||||
return 0, wrapDialError(response, err)
|
||||
|
||||
Reference in New Issue
Block a user