Compare commits

..

17 Commits

Author SHA1 Message Date
世界
0348ace253 Initial release 2022-08-26 16:40:37 +08:00
世界
c5e38203eb Fix read DNS message 2022-08-26 13:35:27 +08:00
世界
9ac31d0233 Fix ipv6 route on linux 2022-08-26 12:30:31 +08:00
世界
9d8d1cd69d Update documentation 2022-08-26 11:10:02 +08:00
世界
07a0381f8b Cleanup vmessws 2022-08-26 10:22:29 +08:00
世界
f841459004 Cleanup vmesshttp 2022-08-26 08:41:45 +08:00
世界
78a26fc139 Update documentation 2022-08-25 22:49:23 +08:00
世界
9f6628445e Improve ip_cidr rule 2022-08-25 22:23:26 +08:00
世界
fa017b5977 Add contributing documentation 2022-08-25 21:08:29 +08:00
世界
58f4a970f2 Fix route connections 2022-08-25 20:48:59 +08:00
世界
021aa8faed Fix ipv6 route on linux 2022-08-25 18:57:36 +08:00
世界
83f6e037d6 Fix http proxy with compressed response 2022-08-25 18:40:13 +08:00
世界
baf153434d Fix issue template 2022-08-25 18:40:13 +08:00
世界
d481bd7993 Fix bind_address 2022-08-25 14:50:10 +08:00
Steven Tang
e859c0a6ef Fix typo in features.md (#32) 2022-08-25 13:42:22 +08:00
zakuwaki
59a39e66b1 Add trojan fallback for ALPN #31 2022-08-25 13:37:32 +08:00
世界
fd5ac69a35 Let vmess use zero instead of auto if TLS enabled 2022-08-25 11:51:17 +08:00
56 changed files with 503 additions and 887 deletions

View File

@@ -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: |-

View File

@@ -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

View File

@@ -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
}

View File

@@ -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 "

View File

@@ -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(),

View File

@@ -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))

View File

@@ -1,6 +1,6 @@
package constant
var (
Version = "1.0"
Version = "1.0-beta1"
Commit = ""
)

View File

@@ -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.

View File

@@ -1,3 +1,5 @@
# DNS
### Structure
```json

View File

@@ -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

View File

@@ -47,7 +47,7 @@ DNS 服务器的地址。
!!! warning ""
默认安装不包含 QUIC 和 HTTP3 传输层,请参阅 [安装](/zh/#installation)。
默认安装不包含 QUIC 和 HTTP3 传输层,请参阅 [安装](/zh/#_2)。
!!! info ""

View File

@@ -1,3 +1,5 @@
# Experimental
### Structure
```json

View File

@@ -18,7 +18,7 @@
!!! error ""
默认安装不包含 Clash API参阅 [安装](/zh/#installation)。
默认安装不包含 Clash API参阅 [安装](/zh/#_2)。
!!! note ""

View File

@@ -32,7 +32,7 @@
!!! warning ""
默认安装不包含被 Hysteria 依赖的 QUIC参阅 [安装](/zh/#installation)。
默认安装不包含被 Hysteria 依赖的 QUIC参阅 [安装](/zh/#_2)。
### Hysteria 字段

View File

@@ -1,3 +1,5 @@
# Inbound
### Structure
```json

View File

@@ -30,7 +30,7 @@
!!! warning ""
默认安装不包含 HTTP3 传输层, 参阅 [安装](/zh/#installation)。
默认安装不包含 HTTP3 传输层, 参阅 [安装](/zh/#_2)。
### Naive 字段

View File

@@ -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

View File

@@ -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

View File

@@ -107,7 +107,7 @@ TCP/IP 栈。
!!! warning ""
默认安装不包含 LWIP 栈,请参阅 [安装](/zh/#installation)。
默认安装不包含 LWIP 栈,请参阅 [安装](/zh/#_2)。
#### include_uid

View File

@@ -38,7 +38,7 @@
!!! warning ""
默认安装不包含被 Hysteria 依赖的 QUIC参阅 [安装](/zh/#installation)。
默认安装不包含被 Hysteria 依赖的 QUIC参阅 [安装](/zh/#_2)。
### Hysteria 字段

View File

@@ -1,3 +1,5 @@
# Outbound
### Structure
```json

View File

@@ -30,7 +30,7 @@
!!! info ""
默认安装不包含嵌入式 Tor, 参阅 [安装](/zh/#installation)。
默认安装不包含嵌入式 Tor, 参阅 [安装](/zh/#_2)。
### Tor 字段

View File

@@ -35,7 +35,7 @@
!!! warning ""
默认安装不包含 WireGuard, 参阅 [安装](/zh/#installation)。
默认安装不包含 WireGuard, 参阅 [安装](/zh/#_2)。
### WireGuard 字段

View File

@@ -1,3 +1,5 @@
# Route
### Structure
```json

View File

@@ -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

View File

@@ -32,7 +32,7 @@
!!! warning ""
默认安装不包含 ACME参阅 [安装](/zh/#installation)。
默认安装不包含 ACME参阅 [安装](/zh/#_2)。
### 出站

View File

@@ -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:

View File

@@ -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
{

View 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
```

View 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.

View 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.

View File

@@ -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 |

View File

@@ -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

View File

@@ -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` 下。

View File

@@ -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()
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
View File

@@ -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
View File

@@ -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=

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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()

View File

@@ -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
}

View File

@@ -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()

View File

@@ -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
}

View File

@@ -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:

View File

@@ -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 {

View File

@@ -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
}
}

View File

@@ -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)