Compare commits

..

1 Commits

Author SHA1 Message Date
世界
f48f8c5d1c Add daemon support 2022-08-25 11:07:41 +08:00
318 changed files with 5639 additions and 24221 deletions

View File

@@ -1,5 +1,6 @@
name: Bug Report name: Bug Report
description: "Create a report to help us improve." description: "Create a report to help us improve."
labels: [ bug ]
body: body:
- type: checkboxes - type: checkboxes
id: terms id: terms
@@ -31,7 +32,7 @@ body:
<details> <details>
```console ```console
$ sing-box version $ sing-box --version
# Paste output here # Paste output here
``` ```
@@ -55,7 +56,7 @@ body:
required: true required: true
- type: textarea - type: textarea
id: log id: config
attributes: attributes:
label: Server and client log file label: Server and client log file
value: |- value: |-

View File

@@ -1,5 +1,10 @@
#!/usr/bin/env bash #!/usr/bin/env bash
PROJECTS=$(dirname "$0")/../.. PROJECTS=$(dirname "$0")/../..
go get -x github.com/sagernet/$1@$(git -C $PROJECTS/$1 rev-parse HEAD)
go get -x github.com/sagernet/sing@$(git -C $PROJECTS/sing rev-parse HEAD)
go get -x github.com/sagernet/sing-dns@$(git -C $PROJECTS/sing-dns rev-parse HEAD)
go get -x github.com/sagernet/sing-tun@$(git -C $PROJECTS/sing-tun rev-parse HEAD)
go get -x github.com/sagernet/sing-shadowsocks@$(git -C $PROJECTS/sing-shadowsocks rev-parse HEAD)
go get -x github.com/sagernet/sing-vmess@$(git -C $PROJECTS/sing-vmess rev-parse HEAD)
go mod tidy go mod tidy

View File

@@ -3,18 +3,14 @@ name: Debug build
on: on:
push: push:
branches: branches:
- main
- dev - dev
- dev-next
paths-ignore: paths-ignore:
- '**.md' - '**.md'
- '.github/**' - '.github/**'
- '!.github/workflows/debug.yml' - '!.github/workflows/debug.yml'
pull_request: pull_request:
branches: branches:
- main
- dev - dev
- dev-next
jobs: jobs:
build: build:

View File

@@ -1,43 +0,0 @@
name: Build Docker Images
on:
push:
tags:
- v*
workflow_dispatch:
inputs:
tag:
description: "The tag version you want to build"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker metadata
id: metadata
uses: docker/metadata-action@v3
with:
images: ghcr.io/sagernet/sing-box
- name: Get tag to build
id: tag
run: |
if [[ -z "${{ github.event.inputs.tag }}" ]]; then
echo ::set-output name=tag::ghcr.io/sagernet/sing-box:${{ github.ref_name }}
else
echo ::set-output name=tag::ghcr.io/sagernet/sing-box:${{ github.event.inputs.tag }}
fi
- name: Build and release Docker images
uses: docker/build-push-action@v2
with:
platforms: linux/386,linux/amd64
target: dist
tags: ${{ steps.tag.outputs.tag }}
push: true

View File

@@ -7,10 +7,6 @@ linters:
- staticcheck - staticcheck
- paralleltest - paralleltest
run:
skip-dirs:
- transport/cloudflaretls
linters-settings: linters-settings:
# gci: # gci:
# sections: # sections:

View File

@@ -13,13 +13,11 @@ builds:
tags: tags:
- with_quic - with_quic
- with_wireguard - with_wireguard
- with_acme
- with_clash_api - with_clash_api
env: env:
- CGO_ENABLED=0 - CGO_ENABLED=0
targets: targets:
- android_arm64
- android_amd64
- android_amd64_v3
- linux_amd64_v1 - linux_amd64_v1
- linux_amd64_v3 - linux_amd64_v3
- linux_arm64 - linux_arm64

View File

@@ -1,23 +0,0 @@
FROM golang:1.19-alpine AS builder
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
COPY . /go/src/github.com/sagernet/sing-box
WORKDIR /go/src/github.com/sagernet/sing-box
ARG GOPROXY=""
ENV GOPROXY ${GOPROXY}
ENV CGO_ENABLED=0
RUN set -ex \
&& apk add git build-base \
&& export COMMIT=$(git rev-parse --short HEAD) \
&& go build -v -trimpath -tags 'no_gvisor,with_quic,with_wireguard,with_acme' \
-o /go/bin/sing-box \
-ldflags "-X github.com/sagernet/sing-box/constant.Commit=${COMMIT} -w -s -buildid=" \
./cmd/sing-box
FROM alpine AS dist
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
RUN [ ! -e /etc/nsswitch.conf ] && echo 'hosts: files dns' > /etc/nsswitch.conf
RUN set -ex \
&& apk upgrade \
&& apk add bash tzdata ca-certificates \
&& rm -rf /var/cache/apk/*
COPY --from=builder /go/bin/sing-box /usr/local/bin/sing-box
ENTRYPOINT ["sing-box"]

View File

@@ -1,6 +1,6 @@
NAME = sing-box NAME = sing-box
COMMIT = $(shell git rev-parse --short HEAD) COMMIT = $(shell git rev-parse --short HEAD)
TAGS ?= with_quic,with_wireguard,with_clash_api TAGS ?= with_quic,with_wireguard,with_clash_api,with_daemon
PARAMS = -v -trimpath -tags '$(TAGS)' -ldflags \ PARAMS = -v -trimpath -tags '$(TAGS)' -ldflags \
'-X "github.com/sagernet/sing-box/constant.Commit=$(COMMIT)" \ '-X "github.com/sagernet/sing-box/constant.Commit=$(COMMIT)" \
-w -s -buildid=' -w -s -buildid='
@@ -49,14 +49,7 @@ snapshot:
ghr --delete --draft --prerelease -p 1 nightly dist/release ghr --delete --draft --prerelease -p 1 nightly dist/release
rm -r dist rm -r dist
release: snapshot_install:
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/goreleaser/goreleaser@latest
go install -v github.com/tcnksm/ghr@latest go install -v github.com/tcnksm/ghr@latest
@@ -64,7 +57,7 @@ test:
@go test -v . && \ @go test -v . && \
pushd test && \ pushd test && \
go mod tidy && \ go mod tidy && \
go test -v -tags with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_shadowsocksr . && \ go test -v -tags with_quic,with_wireguard,with_grpc . && \
popd popd
clean: clean:

View File

@@ -9,22 +9,18 @@ import (
type ClashServer interface { type ClashServer interface {
Service Service
Mode() string TrafficController
StoreSelected() bool
CacheFile() ClashCacheFile
RoutedConnection(ctx context.Context, conn net.Conn, metadata InboundContext, matchedRule Rule) (net.Conn, Tracker)
RoutedPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext, matchedRule Rule) (N.PacketConn, Tracker)
}
type ClashCacheFile interface {
LoadSelected(group string) string
StoreSelected(group string, selected string) error
} }
type Tracker interface { type Tracker interface {
Leave() Leave()
} }
type TrafficController interface {
RoutedConnection(ctx context.Context, conn net.Conn, metadata InboundContext, matchedRule Rule) (net.Conn, Tracker)
RoutedPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext, matchedRule Rule) (N.PacketConn, Tracker)
}
type OutboundGroup interface { type OutboundGroup interface {
Now() string Now() string
All() []string All() []string

View File

@@ -2,13 +2,11 @@ package adapter
import ( import (
"context" "context"
"net"
"net/netip" "net/netip"
"github.com/sagernet/sing-box/common/process" "github.com/sagernet/sing-box/common/process"
"github.com/sagernet/sing-dns" "github.com/sagernet/sing-dns"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
) )
type Inbound interface { type Inbound interface {
@@ -17,13 +15,6 @@ type Inbound interface {
Tag() string Tag() string
} }
type InjectableInbound interface {
Inbound
Network() []string
NewConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error
NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
}
type InboundContext struct { type InboundContext struct {
Inbound string Inbound string
InboundType string InboundType string
@@ -38,8 +29,6 @@ type InboundContext struct {
// cache // cache
InboundDetour string
LastInbound string
OriginDestination M.Socksaddr OriginDestination M.Socksaddr
DomainStrategy dns.DomainStrategy DomainStrategy dns.DomainStrategy
SniffEnabled bool SniffEnabled bool

View File

@@ -39,9 +39,7 @@ type Router interface {
InterfaceMonitor() tun.DefaultInterfaceMonitor InterfaceMonitor() tun.DefaultInterfaceMonitor
PackageManager() tun.PackageManager PackageManager() tun.PackageManager
Rules() []Rule Rules() []Rule
SetTrafficController(controller TrafficController)
ClashServer() ClashServer
SetClashServer(controller ClashServer)
} }
type Rule interface { type Rule interface {

4
box.go
View File

@@ -138,7 +138,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
} }
outbounds = append(outbounds, out) outbounds = append(outbounds, out)
} }
err = router.Initialize(inbounds, outbounds, func() adapter.Outbound { err = router.Initialize(outbounds, func() adapter.Outbound {
out, oErr := outbound.New(ctx, router, logFactory.NewLogger("outbound/direct"), option.Outbound{Type: "direct", Tag: "default"}) out, oErr := outbound.New(ctx, router, logFactory.NewLogger("outbound/direct"), option.Outbound{Type: "direct", Tag: "default"})
common.Must(oErr) common.Must(oErr)
outbounds = append(outbounds, out) outbounds = append(outbounds, out)
@@ -154,7 +154,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
if err != nil { if err != nil {
return nil, E.Cause(err, "create clash api server") return nil, E.Cause(err, "create clash api server")
} }
router.SetClashServer(clashServer) router.SetTrafficController(clashServer)
} }
return &Box{ return &Box{
router: router, router: router,

272
cmd/sing-box/cmd_daemon.go Normal file
View File

@@ -0,0 +1,272 @@
//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 format() error {
return E.Cause(err, "read config") return E.Cause(err, "read config")
} }
var options option.Options var options option.Options
err = options.UnmarshalJSON(configContent) err = json.Unmarshal(configContent, &options)
if err != nil { if err != nil {
return E.Cause(err, "decode config") return E.Cause(err, "decode config")
} }

View File

@@ -9,6 +9,7 @@ import (
"syscall" "syscall"
"github.com/sagernet/sing-box" "github.com/sagernet/sing-box"
"github.com/sagernet/sing-box/common/json"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
@@ -45,7 +46,7 @@ func readConfig() (option.Options, error) {
return option.Options{}, E.Cause(err, "read config") return option.Options{}, E.Cause(err, "read config")
} }
var options option.Options var options option.Options
err = options.UnmarshalJSON(configContent) err = json.Unmarshal(configContent, &options)
if err != nil { if err != nil {
return option.Options{}, E.Cause(err, "decode config") return option.Options{}, E.Cause(err, "decode config")
} }

View File

@@ -38,7 +38,7 @@ func printVersion(cmd *cobra.Command, args []string) {
version += runtime.Version() version += runtime.Version()
version += ", " version += ", "
version += runtime.GOOS version += runtime.GOOS
version += "/" version += ", "
version += runtime.GOARCH version += runtime.GOARCH
version += ", " version += ", "
version += "CGO " version += "CGO "

View File

@@ -1,62 +0,0 @@
package baderror
import (
"context"
"io"
"net"
"strings"
E "github.com/sagernet/sing/common/exceptions"
)
func Contains(err error, msgList ...string) bool {
for _, msg := range msgList {
if strings.Contains(err.Error(), msg) {
return true
}
}
return false
}
func WrapH2(err error) error {
if err == nil {
return nil
}
err = E.Unwrap(err)
if err == io.ErrUnexpectedEOF {
return io.EOF
}
if Contains(err, "client disconnected", "body closed by handler") {
return net.ErrClosed
}
return err
}
func WrapGRPC(err error) error {
// grpc uses stupid internal error types
if err == nil {
return nil
}
if Contains(err, "EOF") {
return io.EOF
}
if Contains(err, "Canceled") {
return context.Canceled
}
if Contains(err,
"the client connection is closing",
"server closed the stream without sending trailers") {
return net.ErrClosed
}
return err
}
func WrapQUIC(err error) error {
if err == nil {
return nil
}
if Contains(err, "canceled with error code 0") {
return net.ErrClosed
}
return err
}

19
common/dialer/bind.go Normal file
View File

@@ -0,0 +1,19 @@
package dialer
import (
"syscall"
"github.com/sagernet/sing/common/control"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
func skipIfPrivate(next control.Func) control.Func {
return func(network, address string, conn syscall.RawConn) error {
destination := M.ParseSocksaddr(address)
if !N.IsPublicAddr(destination.Addr) {
return nil
}
return next(network, address, conn)
}
}

View File

@@ -11,7 +11,6 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common/control" "github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
@@ -65,25 +64,25 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
var listener net.ListenConfig var listener net.ListenConfig
if options.BindInterface != "" { if options.BindInterface != "" {
warnBindInterfaceOnUnsupportedPlatform.Check() warnBindInterfaceOnUnsupportedPlatform.Check()
bindFunc := control.BindToInterface(router.InterfaceBindManager(), options.BindInterface) bindFunc := skipIfPrivate(control.BindToInterface(router.InterfaceBindManager(), options.BindInterface))
dialer.Control = control.Append(dialer.Control, bindFunc) dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc) listener.Control = control.Append(listener.Control, bindFunc)
} else if router.AutoDetectInterface() { } else if router.AutoDetectInterface() {
if C.IsWindows { if C.IsWindows {
bindFunc := control.BindToInterfaceIndexFunc(func(network, address string) int { bindFunc := skipIfPrivate(control.BindToInterfaceIndexFunc(func() int {
return router.InterfaceMonitor().DefaultInterfaceIndex(M.ParseSocksaddr(address).Addr) return router.InterfaceMonitor().DefaultInterfaceIndex()
}) }))
dialer.Control = control.Append(dialer.Control, bindFunc) dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc) listener.Control = control.Append(listener.Control, bindFunc)
} else { } else {
bindFunc := control.BindToInterfaceFunc(router.InterfaceBindManager(), func(network, address string) string { bindFunc := skipIfPrivate(control.BindToInterfaceFunc(router.InterfaceBindManager(), func() string {
return router.InterfaceMonitor().DefaultInterfaceName(M.ParseSocksaddr(address).Addr) return router.InterfaceMonitor().DefaultInterfaceName()
}) }))
dialer.Control = control.Append(dialer.Control, bindFunc) dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc) listener.Control = control.Append(listener.Control, bindFunc)
} }
} else if router.DefaultInterface() != "" { } else if router.DefaultInterface() != "" {
bindFunc := control.BindToInterface(router.InterfaceBindManager(), router.DefaultInterface()) bindFunc := skipIfPrivate(control.BindToInterface(router.InterfaceBindManager(), router.DefaultInterface()))
dialer.Control = control.Append(dialer.Control, bindFunc) dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc) listener.Control = control.Append(listener.Control, bindFunc)
} }
@@ -112,16 +111,9 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
if options.TCPFastOpen { if options.TCPFastOpen {
warnTFOOnUnsupportedPlatform.Check() warnTFOOnUnsupportedPlatform.Check()
} }
if !options.UDPFragment {
dialer.Control = control.Append(dialer.Control, control.DisableUDPFragment())
listener.Control = control.Append(listener.Control, control.DisableUDPFragment())
}
var bindUDPAddr string var bindUDPAddr string
udpDialer := dialer udpDialer := dialer
var bindAddress netip.Addr bindAddress := netip.Addr(options.BindAddress)
if options.BindAddress != nil {
bindAddress = options.BindAddress.Build()
}
if bindAddress.IsValid() { if bindAddress.IsValid() {
dialer.LocalAddr = &net.TCPAddr{ dialer.LocalAddr = &net.TCPAddr{
IP: bindAddress.AsSlice(), IP: bindAddress.AsSlice(),
@@ -135,9 +127,6 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
} }
func (d *DefaultDialer) DialContext(ctx context.Context, network string, address M.Socksaddr) (net.Conn, error) { func (d *DefaultDialer) DialContext(ctx context.Context, network string, address M.Socksaddr) (net.Conn, error) {
if !address.IsValid() {
return nil, E.New("invalid address")
}
switch N.NetworkName(network) { switch N.NetworkName(network) {
case N.NetworkUDP: case N.NetworkUDP:
return d.udpDialer.DialContext(ctx, network, address.String()) return d.udpDialer.DialContext(ctx, network, address.String())

View File

@@ -10,12 +10,15 @@ import (
) )
func New(router adapter.Router, options option.DialerOptions) N.Dialer { func New(router adapter.Router, options option.DialerOptions) N.Dialer {
var dialer N.Dialer
if options.Detour == "" { if options.Detour == "" {
dialer = NewDefault(router, options) return NewDefault(router, options)
} else { } else {
dialer = NewDetour(router, options.Detour) return NewDetour(router, options.Detour)
} }
}
func NewOutbound(router adapter.Router, options option.OutboundDialerOptions) N.Dialer {
dialer := New(router, options.DialerOptions)
domainStrategy := dns.DomainStrategy(options.DomainStrategy) domainStrategy := dns.DomainStrategy(options.DomainStrategy)
if domainStrategy != dns.DomainStrategyAsIS || options.Detour == "" { if domainStrategy != dns.DomainStrategyAsIS || options.Detour == "" {
dialer = NewResolveDialer(router, dialer, domainStrategy, time.Duration(options.FallbackDelay)) dialer = NewResolveDialer(router, dialer, domainStrategy, time.Duration(options.FallbackDelay))

View File

@@ -51,7 +51,7 @@ func (d *ResolveDialer) DialContext(ctx context.Context, network string, destina
} }
func (d *ResolveDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (d *ResolveDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
if !destination.IsFqdn() { if !destination.IsFqdn() || destination.Fqdn == "" {
return d.dialer.ListenPacket(ctx, destination) return d.dialer.ListenPacket(ctx, destination)
} }
ctx, metadata := adapter.AppendContext(ctx) ctx, metadata := adapter.AppendContext(ctx)

View File

@@ -1,26 +1,34 @@
package tls package dialer
import ( import (
"context"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"net" "net"
"net/netip" "net/netip"
"os" "os"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
) )
type stdClientConfig struct { type TLSDialer struct {
dialer N.Dialer
config *tls.Config config *tls.Config
} }
func newStdClient(serverAddress string, options option.OutboundTLSOptions) (Config, error) { func TLSConfig(serverAddress string, options option.OutboundTLSOptions) (*tls.Config, error) {
if !options.Enabled {
return nil, nil
}
var serverName string var serverName string
if options.ServerName != "" { if options.ServerName != "" {
serverName = options.ServerName serverName = options.ServerName
} else if serverAddress != "" { } else if serverAddress != "" {
if _, err := netip.ParseAddr(serverName); err != nil { if _, err := netip.ParseAddr(serverName); err == nil {
serverName = serverAddress serverName = serverAddress
} }
} }
@@ -54,14 +62,14 @@ func newStdClient(serverAddress string, options option.OutboundTLSOptions) (Conf
tlsConfig.NextProtos = options.ALPN tlsConfig.NextProtos = options.ALPN
} }
if options.MinVersion != "" { if options.MinVersion != "" {
minVersion, err := ParseTLSVersion(options.MinVersion) minVersion, err := option.ParseTLSVersion(options.MinVersion)
if err != nil { if err != nil {
return nil, E.Cause(err, "parse min_version") return nil, E.Cause(err, "parse min_version")
} }
tlsConfig.MinVersion = minVersion tlsConfig.MinVersion = minVersion
} }
if options.MaxVersion != "" { if options.MaxVersion != "" {
maxVersion, err := ParseTLSVersion(options.MaxVersion) maxVersion, err := option.ParseTLSVersion(options.MaxVersion)
if err != nil { if err != nil {
return nil, E.Cause(err, "parse max_version") return nil, E.Cause(err, "parse max_version")
} }
@@ -96,21 +104,42 @@ func newStdClient(serverAddress string, options option.OutboundTLSOptions) (Conf
} }
tlsConfig.RootCAs = certPool tlsConfig.RootCAs = certPool
} }
return &stdClientConfig{&tlsConfig}, nil return &tlsConfig, nil
} }
func (s *stdClientConfig) NextProtos() []string { func NewTLS(dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
return s.config.NextProtos if !options.Enabled {
return dialer, nil
}
tlsConfig, err := TLSConfig(serverAddress, options)
if err != nil {
return nil, err
}
return &TLSDialer{
dialer: dialer,
config: tlsConfig,
}, nil
} }
func (s *stdClientConfig) SetNextProtos(nextProto []string) { func (d *TLSDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
s.config.NextProtos = nextProto if network != N.NetworkTCP {
return nil, os.ErrInvalid
}
conn, err := d.dialer.DialContext(ctx, network, destination)
if err != nil {
return nil, err
}
return TLSClient(ctx, conn, d.config)
} }
func (s *stdClientConfig) Config() (*STDConfig, error) { func (d *TLSDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
return s.config, nil return nil, os.ErrInvalid
} }
func (s *stdClientConfig) Client(conn net.Conn) Conn { func TLSClient(ctx context.Context, conn net.Conn, tlsConfig *tls.Config) (*tls.Conn, error) {
return tls.Client(conn, s.config) tlsConn := tls.Client(conn, tlsConfig)
ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
defer cancel()
err := tlsConn.HandshakeContext(ctx)
return tlsConn, err
} }

View File

@@ -1,128 +0,0 @@
package json
import (
"bufio"
"io"
)
// kanged from v2ray
type commentFilterState = byte
const (
commentFilterStateContent commentFilterState = iota
commentFilterStateEscape
commentFilterStateDoubleQuote
commentFilterStateDoubleQuoteEscape
commentFilterStateSingleQuote
commentFilterStateSingleQuoteEscape
commentFilterStateComment
commentFilterStateSlash
commentFilterStateMultilineComment
commentFilterStateMultilineCommentStar
)
type CommentFilter struct {
br *bufio.Reader
state commentFilterState
}
func NewCommentFilter(reader io.Reader) io.Reader {
return &CommentFilter{br: bufio.NewReader(reader)}
}
func (v *CommentFilter) Read(b []byte) (int, error) {
p := b[:0]
for len(p) < len(b)-2 {
x, err := v.br.ReadByte()
if err != nil {
if len(p) == 0 {
return 0, err
}
return len(p), nil
}
switch v.state {
case commentFilterStateContent:
switch x {
case '"':
v.state = commentFilterStateDoubleQuote
p = append(p, x)
case '\'':
v.state = commentFilterStateSingleQuote
p = append(p, x)
case '\\':
v.state = commentFilterStateEscape
case '#':
v.state = commentFilterStateComment
case '/':
v.state = commentFilterStateSlash
default:
p = append(p, x)
}
case commentFilterStateEscape:
p = append(p, '\\', x)
v.state = commentFilterStateContent
case commentFilterStateDoubleQuote:
switch x {
case '"':
v.state = commentFilterStateContent
p = append(p, x)
case '\\':
v.state = commentFilterStateDoubleQuoteEscape
default:
p = append(p, x)
}
case commentFilterStateDoubleQuoteEscape:
p = append(p, '\\', x)
v.state = commentFilterStateDoubleQuote
case commentFilterStateSingleQuote:
switch x {
case '\'':
v.state = commentFilterStateContent
p = append(p, x)
case '\\':
v.state = commentFilterStateSingleQuoteEscape
default:
p = append(p, x)
}
case commentFilterStateSingleQuoteEscape:
p = append(p, '\\', x)
v.state = commentFilterStateSingleQuote
case commentFilterStateComment:
if x == '\n' {
v.state = commentFilterStateContent
p = append(p, '\n')
}
case commentFilterStateSlash:
switch x {
case '/':
v.state = commentFilterStateComment
case '*':
v.state = commentFilterStateMultilineComment
default:
p = append(p, '/', x)
}
case commentFilterStateMultilineComment:
switch x {
case '*':
v.state = commentFilterStateMultilineCommentStar
case '\n':
p = append(p, '\n')
}
case commentFilterStateMultilineCommentStar:
switch x {
case '/':
v.state = commentFilterStateContent
case '*':
// Stay
case '\n':
p = append(p, '\n')
default:
v.state = commentFilterStateMultilineComment
}
default:
panic("Unknown state.")
}
}
return len(p), nil
}

View File

@@ -18,21 +18,21 @@ func NewSearcher(config Config) (Searcher, error) {
} }
func (s *androidSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) { func (s *androidSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
_, uid, err := resolveSocketByNetlink(network, source, destination) socket, err := resolveSocketByNetlink(network, source, destination)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if sharedPackage, loaded := s.packageManager.SharedPackageByID(uid % 100000); loaded { if sharedPackage, loaded := s.packageManager.SharedPackageByID(socket.UID); loaded {
return &Info{ return &Info{
UserId: int32(uid), UserId: int32(socket.UID),
PackageName: sharedPackage, PackageName: sharedPackage,
}, nil }, nil
} }
if packageName, loaded := s.packageManager.PackageByID(uid % 100000); loaded { if packageName, loaded := s.packageManager.PackageByID(socket.UID); loaded {
return &Info{ return &Info{
UserId: int32(uid), UserId: int32(socket.UID),
PackageName: packageName, PackageName: packageName,
}, nil }, nil
} }
return &Info{UserId: int32(uid)}, nil return &Info{UserId: int32(socket.UID)}, nil
} }

View File

@@ -20,16 +20,16 @@ func NewSearcher(config Config) (Searcher, error) {
} }
func (s *linuxSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) { func (s *linuxSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
inode, uid, err := resolveSocketByNetlink(network, source, destination) socket, err := resolveSocketByNetlink(network, source, destination)
if err != nil { if err != nil {
return nil, err return nil, err
} }
processPath, err := resolveProcessNameByProcSearch(inode, uid) processPath, err := resolveProcessNameByProcSearch(socket.INode, socket.UID)
if err != nil { if err != nil {
s.logger.DebugContext(ctx, "find process path: ", err) s.logger.DebugContext(ctx, "find process path: ", err)
} }
return &Info{ return &Info{
UserId: int32(uid), UserId: int32(socket.UID),
ProcessPath: processPath, ProcessPath: processPath,
}, nil }, nil
} }

View File

@@ -6,7 +6,6 @@ import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"net"
"net/netip" "net/netip"
"os" "os"
"path" "path"
@@ -15,9 +14,7 @@ import (
"unicode" "unicode"
"unsafe" "unsafe"
"github.com/sagernet/sing/common" "github.com/sagernet/netlink"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
) )
@@ -37,7 +34,7 @@ const (
pathProc = "/proc" pathProc = "/proc"
) )
func resolveSocketByNetlink(network string, source netip.AddrPort, destination netip.AddrPort) (inode, uid uint32, err error) { func resolveSocketByNetlink(network string, source netip.AddrPort, destination netip.AddrPort) (*netlink.Socket, error) {
var family uint8 var family uint8
var protocol uint8 var protocol uint8
@@ -47,110 +44,28 @@ func resolveSocketByNetlink(network string, source netip.AddrPort, destination n
case N.NetworkUDP: case N.NetworkUDP:
protocol = syscall.IPPROTO_UDP protocol = syscall.IPPROTO_UDP
default: default:
return 0, 0, os.ErrInvalid return nil, os.ErrInvalid
} }
if source.Addr().Is4() { if source.Addr().Is4() {
family = syscall.AF_INET family = syscall.AF_INET
} else { } else {
family = syscall.AF_INET6 family = syscall.AF_INET6
} }
sockets, err := netlink.SocketGet(family, protocol, source, netip.AddrPortFrom(netip.IPv6Unspecified(), 0))
req := packSocketDiagRequest(family, protocol, source) if err == nil {
sockets, err = netlink.SocketGet(family, protocol, source, destination)
socket, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_DGRAM, syscall.NETLINK_INET_DIAG) }
if err != nil { if err != nil {
return 0, 0, E.Cause(err, "dial netlink") return nil, err
} }
defer syscall.Close(socket) if len(sockets) > 1 {
for _, socket := range sockets {
syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, &syscall.Timeval{Usec: 100}) if socket.ID.DestinationPort == destination.Port() {
syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &syscall.Timeval{Usec: 100}) return socket, nil
}
err = syscall.Connect(socket, &syscall.SockaddrNetlink{ }
Family: syscall.AF_NETLINK,
Pad: 0,
Pid: 0,
Groups: 0,
})
if err != nil {
return
} }
return sockets[0], nil
_, err = syscall.Write(socket, req)
if err != nil {
return 0, 0, E.Cause(err, "write netlink request")
}
_buffer := buf.StackNew()
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
n, err := syscall.Read(socket, buffer.FreeBytes())
if err != nil {
return 0, 0, E.Cause(err, "read netlink response")
}
buffer.Truncate(n)
messages, err := syscall.ParseNetlinkMessage(buffer.Bytes())
if err != nil {
return 0, 0, E.Cause(err, "parse netlink message")
} else if len(messages) == 0 {
return 0, 0, E.New("unexcepted netlink response")
}
message := messages[0]
if message.Header.Type&syscall.NLMSG_ERROR != 0 {
return 0, 0, E.New("netlink message: NLMSG_ERROR")
}
inode, uid = unpackSocketDiagResponse(&messages[0])
return
}
func packSocketDiagRequest(family, protocol byte, source netip.AddrPort) []byte {
s := make([]byte, 16)
copy(s, source.Addr().AsSlice())
buf := make([]byte, sizeOfSocketDiagRequest)
nativeEndian.PutUint32(buf[0:4], sizeOfSocketDiagRequest)
nativeEndian.PutUint16(buf[4:6], socketDiagByFamily)
nativeEndian.PutUint16(buf[6:8], syscall.NLM_F_REQUEST|syscall.NLM_F_DUMP)
nativeEndian.PutUint32(buf[8:12], 0)
nativeEndian.PutUint32(buf[12:16], 0)
buf[16] = family
buf[17] = protocol
buf[18] = 0
buf[19] = 0
nativeEndian.PutUint32(buf[20:24], 0xFFFFFFFF)
binary.BigEndian.PutUint16(buf[24:26], source.Port())
binary.BigEndian.PutUint16(buf[26:28], 0)
copy(buf[28:44], s)
copy(buf[44:60], net.IPv6zero)
nativeEndian.PutUint32(buf[60:64], 0)
nativeEndian.PutUint64(buf[64:72], 0xFFFFFFFFFFFFFFFF)
return buf
}
func unpackSocketDiagResponse(msg *syscall.NetlinkMessage) (inode, uid uint32) {
if len(msg.Data) < 72 {
return 0, 0
}
data := msg.Data
uid = nativeEndian.Uint32(data[64:68])
inode = nativeEndian.Uint32(data[68:72])
return
} }
func resolveProcessNameByProcSearch(inode, uid uint32) (string, error) { func resolveProcessNameByProcSearch(inode, uid uint32) (string, error) {

View File

@@ -13,7 +13,6 @@ import (
type Listener struct { type Listener struct {
net.Listener net.Listener
AcceptNoHeader bool
} }
func (l *Listener) Accept() (net.Conn, error) { func (l *Listener) Accept() (net.Conn, error) {
@@ -23,7 +22,7 @@ func (l *Listener) Accept() (net.Conn, error) {
} }
bufReader := std_bufio.NewReader(conn) bufReader := std_bufio.NewReader(conn)
header, err := proxyproto.Read(bufReader) header, err := proxyproto.Read(bufReader)
if err != nil && !(l.AcceptNoHeader && err == proxyproto.ErrNoProxyProtocol) { if err != nil {
return nil, err return nil, err
} }
if bufReader.Buffered() > 0 { if bufReader.Buffered() > 0 {
@@ -34,11 +33,8 @@ func (l *Listener) Accept() (net.Conn, error) {
} }
conn = bufio.NewCachedConn(conn, cache) conn = bufio.NewCachedConn(conn, cache)
} }
if header != nil { return &bufio.AddrConn{Conn: conn, Metadata: M.Metadata{
return &bufio.AddrConn{Conn: conn, Metadata: M.Metadata{ Source: M.SocksaddrFromNet(header.SourceAddr),
Source: M.SocksaddrFromNet(header.SourceAddr), Destination: M.SocksaddrFromNet(header.DestinationAddr),
Destination: M.SocksaddrFromNet(header.DestinationAddr), }}, nil
}}, nil
}
return conn, nil
} }

View File

@@ -1,64 +0,0 @@
package redir
import (
"net"
"net/netip"
"syscall"
"unsafe"
M "github.com/sagernet/sing/common/metadata"
)
const (
PF_OUT = 0x2
DIOCNATLOOK = 0xc0544417
)
func GetOriginalDestination(conn net.Conn) (destination netip.AddrPort, err error) {
fd, err := syscall.Open("/dev/pf", 0, syscall.O_RDONLY)
if err != nil {
return netip.AddrPort{}, err
}
defer syscall.Close(fd)
nl := struct {
saddr, daddr, rsaddr, rdaddr [16]byte
sxport, dxport, rsxport, rdxport [4]byte
af, proto, protoVariant, direction uint8
}{
af: syscall.AF_INET,
proto: syscall.IPPROTO_TCP,
direction: PF_OUT,
}
la := conn.LocalAddr().(*net.TCPAddr)
ra := conn.RemoteAddr().(*net.TCPAddr)
raIP, laIP := ra.IP, la.IP
raPort, laPort := ra.Port, la.Port
switch {
case raIP.To4() != nil:
copy(nl.saddr[:net.IPv4len], raIP.To4())
copy(nl.daddr[:net.IPv4len], laIP.To4())
nl.af = syscall.AF_INET
default:
copy(nl.saddr[:], raIP.To16())
copy(nl.daddr[:], laIP.To16())
nl.af = syscall.AF_INET6
}
nl.sxport[0], nl.sxport[1] = byte(raPort>>8), byte(raPort)
nl.dxport[0], nl.dxport[1] = byte(laPort>>8), byte(laPort)
if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), DIOCNATLOOK, uintptr(unsafe.Pointer(&nl))); errno != 0 {
return netip.AddrPort{}, errno
}
var ip net.IP
switch nl.af {
case syscall.AF_INET:
ip = make(net.IP, net.IPv4len)
copy(ip, nl.rdaddr[:net.IPv4len])
case syscall.AF_INET6:
ip = make(net.IP, net.IPv6len)
copy(ip, nl.rdaddr[:])
}
port := uint16(nl.rdxport[0])<<8 | uint16(nl.rdxport[1])
destination = netip.AddrPortFrom(M.AddrFromIP(ip), port)
return
}

View File

@@ -1,4 +1,4 @@
//go:build !linux && !darwin //go:build !linux
package redir package redir

View File

@@ -1,7 +1,6 @@
package settings package settings
import ( import (
"net/netip"
"strings" "strings"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@@ -20,8 +19,8 @@ type systemProxy struct {
isMixed bool isMixed bool
} }
func (p *systemProxy) update(event int) error { func (p *systemProxy) update() error {
newInterfaceName := p.monitor.DefaultInterfaceName(netip.IPv4Unspecified()) newInterfaceName := p.monitor.DefaultInterfaceName()
if p.interfaceName == newInterfaceName { if p.interfaceName == newInterfaceName {
return nil return nil
} }
@@ -88,7 +87,7 @@ func SetSystemProxy(router adapter.Router, port uint16, isMixed bool) (func() er
port: port, port: port,
isMixed: isMixed, isMixed: isMixed,
} }
err := proxy.update(tun.EventInterfaceUpdate) err := proxy.update()
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -22,7 +22,7 @@ func StreamDomainNameQuery(readCtx context.Context, reader io.Reader) (*adapter.
if err != nil { if err != nil {
return nil, err return nil, err
} }
if length == 0 { if length > 512 {
return nil, os.ErrInvalid return nil, os.ErrInvalid
} }
_buffer := buf.StackNewSize(int(length)) _buffer := buf.StackNewSize(int(length))

View File

@@ -1,63 +0,0 @@
package tls
import (
"context"
"net"
"os"
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
func NewDialerFromOptions(router adapter.Router, dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
config, err := NewClient(router, serverAddress, options)
if err != nil {
return nil, err
}
return NewDialer(dialer, config), nil
}
func NewClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
if options.ECH != nil && options.ECH.Enabled {
return newECHClient(router, serverAddress, options)
} else if options.UTLS != nil && options.UTLS.Enabled {
return newUTLSClient(router, serverAddress, options)
} else {
return newStdClient(serverAddress, options)
}
}
func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) {
tlsConn := config.Client(conn)
ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
defer cancel()
err := tlsConn.HandshakeContext(ctx)
return tlsConn, err
}
type Dialer struct {
dialer N.Dialer
config Config
}
func NewDialer(dialer N.Dialer, config Config) N.Dialer {
return &Dialer{dialer, config}
}
func (d *Dialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
if network != N.NetworkTCP {
return nil, os.ErrInvalid
}
conn, err := d.dialer.DialContext(ctx, network, destination)
if err != nil {
return nil, err
}
return ClientHandshake(ctx, conn, d.config)
}
func (d *Dialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
return nil, os.ErrInvalid
}

View File

@@ -1,12 +0,0 @@
package tls
const (
VersionTLS10 = 0x0301
VersionTLS11 = 0x0302
VersionTLS12 = 0x0303
VersionTLS13 = 0x0304
// Deprecated: SSLv3 is cryptographically broken, and is no longer
// supported by this package. See golang.org/issue/32716.
VersionSSL30 = 0x0300
)

View File

@@ -1,49 +0,0 @@
package tls
import (
"context"
"crypto/tls"
"net"
"github.com/sagernet/sing-box/adapter"
E "github.com/sagernet/sing/common/exceptions"
)
type (
STDConfig = tls.Config
STDConn = tls.Conn
)
type Config interface {
NextProtos() []string
SetNextProtos(nextProto []string)
Config() (*STDConfig, error)
Client(conn net.Conn) Conn
}
type ServerConfig interface {
Config
adapter.Service
Server(conn net.Conn) Conn
}
type Conn interface {
net.Conn
HandshakeContext(ctx context.Context) error
ConnectionState() tls.ConnectionState
}
func ParseTLSVersion(version string) (uint16, error) {
switch version {
case "1.0":
return tls.VersionTLS10, nil
case "1.1":
return tls.VersionTLS11, nil
case "1.2":
return tls.VersionTLS12, nil
case "1.3":
return tls.VersionTLS13, nil
default:
return 0, E.New("unknown tls version:", version)
}
}

View File

@@ -1,219 +0,0 @@
//go:build with_ech
package tls
import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"net"
"net/netip"
"os"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
cftls "github.com/sagernet/sing-box/transport/cloudflaretls"
"github.com/sagernet/sing-dns"
E "github.com/sagernet/sing/common/exceptions"
mDNS "github.com/miekg/dns"
"golang.org/x/net/dns/dnsmessage"
)
type echClientConfig struct {
config *cftls.Config
}
func (e *echClientConfig) NextProtos() []string {
return e.config.NextProtos
}
func (e *echClientConfig) SetNextProtos(nextProto []string) {
e.config.NextProtos = nextProto
}
func (e *echClientConfig) Config() (*STDConfig, error) {
return nil, E.New("unsupported usage for ECH")
}
func (e *echClientConfig) Client(conn net.Conn) Conn {
return &echConnWrapper{cftls.Client(conn, e.config)}
}
type echConnWrapper struct {
*cftls.Conn
}
func (c *echConnWrapper) ConnectionState() tls.ConnectionState {
state := c.Conn.ConnectionState()
return tls.ConnectionState{
Version: state.Version,
HandshakeComplete: state.HandshakeComplete,
DidResume: state.DidResume,
CipherSuite: state.CipherSuite,
NegotiatedProtocol: state.NegotiatedProtocol,
NegotiatedProtocolIsMutual: state.NegotiatedProtocolIsMutual,
ServerName: state.ServerName,
PeerCertificates: state.PeerCertificates,
VerifiedChains: state.VerifiedChains,
SignedCertificateTimestamps: state.SignedCertificateTimestamps,
OCSPResponse: state.OCSPResponse,
TLSUnique: state.TLSUnique,
}
}
func newECHClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
var serverName string
if options.ServerName != "" {
serverName = options.ServerName
} else if serverAddress != "" {
if _, err := netip.ParseAddr(serverName); err != nil {
serverName = serverAddress
}
}
if serverName == "" && !options.Insecure {
return nil, E.New("missing server_name or insecure=true")
}
var tlsConfig cftls.Config
if options.DisableSNI {
tlsConfig.ServerName = "127.0.0.1"
} else {
tlsConfig.ServerName = serverName
}
if options.Insecure {
tlsConfig.InsecureSkipVerify = options.Insecure
} else if options.DisableSNI {
tlsConfig.InsecureSkipVerify = true
tlsConfig.VerifyConnection = func(state cftls.ConnectionState) error {
verifyOptions := x509.VerifyOptions{
DNSName: serverName,
Intermediates: x509.NewCertPool(),
}
for _, cert := range state.PeerCertificates[1:] {
verifyOptions.Intermediates.AddCert(cert)
}
_, err := state.PeerCertificates[0].Verify(verifyOptions)
return err
}
}
if len(options.ALPN) > 0 {
tlsConfig.NextProtos = options.ALPN
}
if options.MinVersion != "" {
minVersion, err := ParseTLSVersion(options.MinVersion)
if err != nil {
return nil, E.Cause(err, "parse min_version")
}
tlsConfig.MinVersion = minVersion
}
if options.MaxVersion != "" {
maxVersion, err := ParseTLSVersion(options.MaxVersion)
if err != nil {
return nil, E.Cause(err, "parse max_version")
}
tlsConfig.MaxVersion = maxVersion
}
if options.CipherSuites != nil {
find:
for _, cipherSuite := range options.CipherSuites {
for _, tlsCipherSuite := range cftls.CipherSuites() {
if cipherSuite == tlsCipherSuite.Name {
tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, tlsCipherSuite.ID)
continue find
}
}
return nil, E.New("unknown cipher_suite: ", cipherSuite)
}
}
var certificate []byte
if options.Certificate != "" {
certificate = []byte(options.Certificate)
} else if options.CertificatePath != "" {
content, err := os.ReadFile(options.CertificatePath)
if err != nil {
return nil, E.Cause(err, "read certificate")
}
certificate = content
}
if len(certificate) > 0 {
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(certificate) {
return nil, E.New("failed to parse certificate:\n\n", certificate)
}
tlsConfig.RootCAs = certPool
}
// ECH Config
tlsConfig.ECHEnabled = true
tlsConfig.PQSignatureSchemesEnabled = options.ECH.PQSignatureSchemesEnabled
tlsConfig.DynamicRecordSizingDisabled = options.ECH.DynamicRecordSizingDisabled
if options.ECH.Config != "" {
clientConfigContent, err := base64.StdEncoding.DecodeString(options.ECH.Config)
if err != nil {
return nil, err
}
clientConfig, err := cftls.UnmarshalECHConfigs(clientConfigContent)
if err != nil {
return nil, err
}
tlsConfig.ClientECHConfigs = clientConfig
} else {
tlsConfig.GetClientECHConfigs = fetchECHClientConfig(router)
}
return &echClientConfig{&tlsConfig}, nil
}
const typeHTTPS = 65
func fetchECHClientConfig(router adapter.Router) func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) {
return func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) {
message := &dnsmessage.Message{
Header: dnsmessage.Header{
RecursionDesired: true,
},
Questions: []dnsmessage.Question{
{
Name: dnsmessage.MustNewName(serverName + "."),
Type: typeHTTPS,
Class: dnsmessage.ClassINET,
},
},
}
response, err := router.Exchange(ctx, message)
if err != nil {
return nil, err
}
if response.RCode != dnsmessage.RCodeSuccess {
return nil, dns.RCodeError(response.RCode)
}
content, err := response.Pack()
if err != nil {
return nil, err
}
var mMsg mDNS.Msg
err = mMsg.Unpack(content)
if err != nil {
return nil, err
}
for _, rr := range mMsg.Answer {
switch resource := rr.(type) {
case *mDNS.HTTPS:
for _, value := range resource.Value {
if value.Key().String() == "ech" {
echConfig, err := base64.StdEncoding.DecodeString(value.String())
if err != nil {
return nil, E.Cause(err, "decode ECH config")
}
return cftls.UnmarshalECHConfigs(echConfig)
}
}
default:
return nil, E.New("unknown resource record type: ", resource.Header().Rrtype)
}
}
return nil, E.New("no ECH config found")
}
}

View File

@@ -1,13 +0,0 @@
//go:build !with_ech
package tls
import (
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
)
func newECHClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
return nil, E.New(`ECH is not included in this build, rebuild with -tags with_ech`)
}

View File

@@ -1,12 +0,0 @@
package tls
import (
"context"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
)
func NewServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
return newSTDServer(ctx, logger, options)
}

View File

@@ -1,151 +0,0 @@
//go:build with_utls
package tls
import (
"context"
"crypto/tls"
"crypto/x509"
"net"
"net/netip"
"os"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
utls "github.com/refraction-networking/utls"
)
type utlsClientConfig struct {
config *utls.Config
id utls.ClientHelloID
}
func (e *utlsClientConfig) NextProtos() []string {
return e.config.NextProtos
}
func (e *utlsClientConfig) SetNextProtos(nextProto []string) {
e.config.NextProtos = nextProto
}
func (e *utlsClientConfig) Config() (*STDConfig, error) {
return nil, E.New("unsupported usage for uTLS")
}
func (e *utlsClientConfig) Client(conn net.Conn) Conn {
return &utlsConnWrapper{utls.UClient(conn, e.config, e.id)}
}
type utlsConnWrapper struct {
*utls.UConn
}
func (c *utlsConnWrapper) HandshakeContext(ctx context.Context) error {
return c.Conn.Handshake()
}
func (c *utlsConnWrapper) ConnectionState() tls.ConnectionState {
state := c.Conn.ConnectionState()
return tls.ConnectionState{
Version: state.Version,
HandshakeComplete: state.HandshakeComplete,
DidResume: state.DidResume,
CipherSuite: state.CipherSuite,
NegotiatedProtocol: state.NegotiatedProtocol,
NegotiatedProtocolIsMutual: state.NegotiatedProtocolIsMutual,
ServerName: state.ServerName,
PeerCertificates: state.PeerCertificates,
VerifiedChains: state.VerifiedChains,
SignedCertificateTimestamps: state.SignedCertificateTimestamps,
OCSPResponse: state.OCSPResponse,
TLSUnique: state.TLSUnique,
}
}
func newUTLSClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
var serverName string
if options.ServerName != "" {
serverName = options.ServerName
} else if serverAddress != "" {
if _, err := netip.ParseAddr(serverName); err != nil {
serverName = serverAddress
}
}
if serverName == "" && !options.Insecure {
return nil, E.New("missing server_name or insecure=true")
}
var tlsConfig utls.Config
if options.DisableSNI {
tlsConfig.ServerName = "127.0.0.1"
} else {
tlsConfig.ServerName = serverName
}
if options.Insecure {
tlsConfig.InsecureSkipVerify = options.Insecure
} else if options.DisableSNI {
return nil, E.New("disable_sni is unsupported in uTLS")
}
if len(options.ALPN) > 0 {
tlsConfig.NextProtos = options.ALPN
}
if options.MinVersion != "" {
minVersion, err := ParseTLSVersion(options.MinVersion)
if err != nil {
return nil, E.Cause(err, "parse min_version")
}
tlsConfig.MinVersion = minVersion
}
if options.MaxVersion != "" {
maxVersion, err := ParseTLSVersion(options.MaxVersion)
if err != nil {
return nil, E.Cause(err, "parse max_version")
}
tlsConfig.MaxVersion = maxVersion
}
if options.CipherSuites != nil {
find:
for _, cipherSuite := range options.CipherSuites {
for _, tlsCipherSuite := range tls.CipherSuites() {
if cipherSuite == tlsCipherSuite.Name {
tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, tlsCipherSuite.ID)
continue find
}
}
return nil, E.New("unknown cipher_suite: ", cipherSuite)
}
}
var certificate []byte
if options.Certificate != "" {
certificate = []byte(options.Certificate)
} else if options.CertificatePath != "" {
content, err := os.ReadFile(options.CertificatePath)
if err != nil {
return nil, E.Cause(err, "read certificate")
}
certificate = content
}
if len(certificate) > 0 {
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(certificate) {
return nil, E.New("failed to parse certificate:\n\n", certificate)
}
tlsConfig.RootCAs = certPool
}
var id utls.ClientHelloID
switch options.UTLS.Fingerprint {
case "chrome", "":
id = utls.HelloChrome_Auto
case "firefox":
id = utls.HelloFirefox_Auto
case "ios":
id = utls.HelloIOS_Auto
case "android":
id = utls.HelloAndroid_11_OkHttp
case "random":
id = utls.HelloRandomized
}
return &utlsClientConfig{&tlsConfig, id}, nil
}

View File

@@ -1,13 +0,0 @@
//go:build !with_utls
package tls
import (
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
)
func newUTLSClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
return nil, E.New(`uTLS is not included in this build, rebuild with -tags with_utls`)
}

View File

@@ -1,26 +1,23 @@
package constant package constant
const ( const (
TypeTun = "tun" TypeTun = "tun"
TypeRedirect = "redirect" TypeRedirect = "redirect"
TypeTProxy = "tproxy" TypeTProxy = "tproxy"
TypeDirect = "direct" TypeDirect = "direct"
TypeBlock = "block" TypeBlock = "block"
TypeDNS = "dns" TypeDNS = "dns"
TypeSocks = "socks" TypeSocks = "socks"
TypeHTTP = "http" TypeHTTP = "http"
TypeMixed = "mixed" TypeMixed = "mixed"
TypeShadowsocks = "shadowsocks" TypeShadowsocks = "shadowsocks"
TypeVMess = "vmess" TypeVMess = "vmess"
TypeTrojan = "trojan" TypeTrojan = "trojan"
TypeNaive = "naive" TypeNaive = "naive"
TypeWireGuard = "wireguard" TypeWireGuard = "wireguard"
TypeHysteria = "hysteria" TypeHysteria = "hysteria"
TypeTor = "tor" TypeTor = "tor"
TypeSSH = "ssh" TypeSSH = "ssh"
TypeShadowTLS = "shadowtls"
TypeShadowsocksR = "shadowsocksr"
TypeVLESS = "vless"
) )
const ( const (

View File

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

View File

@@ -1,157 +1,4 @@
#### 1.1-beta4 #### 2022/08/24
* Add internal simple-obfs and v2ray-plugin [Shadowsocks plugins](/configuration/outbound/shadowsocks#plugin)
* Add [ShadowsocksR outbound](/configuration/outbound/shadowsocksr)
* Add [VLESS outbound and XUDP](/configuration/outbound/vless)
* Skip wait for hysteria tcp handshake response
* Fix socks4 client
* Fix hysteria inbound
* Fix concurrent write
#### 1.0.3
* Fix socks4 client
* Fix hysteria inbound
* Fix concurrent write
#### 1.1-beta3
* Fix using custom TLS client in http2 client
* Fix bugs in 1.1-beta2
#### 1.1-beta2
* Add Clash mode and persistence support **1**
* Add TLS ECH and uTLS support for outbound TLS options **2**
* Fix socks4 request
* Fix processing empty dns result
*1*:
Switching modes using the Clash API, and `store-selected` are now supported,
see [Experimental](/configuration/experimental).
*2*:
ECH (Encrypted Client Hello) is a TLS extension that allows a client to encrypt the first part of its ClientHello
message, see [TLS#ECH](/configuration/shared/tls#ech).
uTLS is a fork of "crypto/tls", which provides ClientHello fingerprinting resistance,
see [TLS#uTLS](/configuration/shared/tls#utls).
#### 1.0.2
* Fix socks4 request
* Fix processing empty dns result
#### 1.1-beta1
* Add support for use with android VPNService **1**
* Add tun support for WireGuard outbound **2**
* Add system tun stack **3**
* Add comment filter for config **4**
* Add option for allow optional proxy protocol header
* Add half close for smux
* Set UDP DF by default **5**
* Set default tun mtu to 9000
* Update gVisor to 20220905.0
*1*:
In previous versions, Android VPN would not work with tun enabled.
The usage of tun over VPN and VPN over tun is now supported, see [Tun Inbound](/configuration/inbound/tun#auto_route).
*2*:
In previous releases, WireGuard outbound support was backed by the lower performance gVisor virtual interface.
It achieves the same performance as wireguard-go by providing automatic system interface support.
*3*:
It does not depend on gVisor and has better performance in some cases.
It is less compatible and may not be available in some environments.
*4*:
Annotated json configuration files are now supported.
*5*:
UDP fragmentation is now blocked by default.
Including shadowsocks-libev, shadowsocks-rust and quic-go all disable segmentation by default.
See [Dial Fields](/configuration/shared/dial#udp_fragment)
and [Listen Fields](/configuration/shared/listen#udp_fragment).
#### 1.0.1
* Fix match 4in6 address in ip_cidr
* Fix clash api log level format error
* Fix clash api unknown proxy type
#### 1.0
* Fix wireguard reconnect
* Fix naive inbound
* Fix json format error message
* Fix processing vmess termination signal
* Fix hysteria stream error
* Fix listener close when proxyproto failed
#### 1.0-rc1
* Fix write log timestamp
* Fix write zero
* Fix dial parallel in direct outbound
* Fix write trojan udp
* Fix DNS routing
* Add attribute support for geosite
* Update documentation for [Dial Fields](/configuration/shared/dial)
#### 1.0-beta3
* Add [chained inbound](/configuration/shared/listen#detour) support
* Add process_path rule item
* Add macOS redirect support
* Add ShadowTLS [Inbound](/configuration/inbound/shadowtls), [Outbound](/configuration/outbound/shadowtls)
and [Examples](/examples/shadowtls)
* Fix search android package in non-owner users
* Fix socksaddr type condition
* Fix smux session status
* Refactor inbound and outbound documentation
* Minor fixes
#### 1.0-beta2
* Add strict_route option for [Tun inbound](/configuration/inbound/tun#strict_route)
* Add packetaddr support for [VMess outbound](/configuration/outbound/vmess#packet_addr)
* Add better performing alternative gRPC implementation
* Add [docker image](https://github.com/SagerNet/sing-box/pkgs/container/sing-box)
* Fix sniff override destination
#### 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 naive padding
* Fix unix search path * Fix unix search path
@@ -160,7 +7,7 @@ and [Listen Fields](/configuration/shared/listen#udp_fragment).
* Fix early close on windows and catch any * Fix early close on windows and catch any
* Initial zh-CN document translation * Initial zh-CN document translation
##### 2022/08/23 #### 2022/08/23
* Add [V2Ray Transport](/configuration/shared/v2ray-transport) support for VMess and Trojan * 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) * Allow plain http request in Naive inbound (It can now be used with nginx)
@@ -169,17 +16,17 @@ and [Listen Fields](/configuration/shared/listen#udp_fragment).
* Parse X-Forward-For in HTTP requests * Parse X-Forward-For in HTTP requests
* Handle SIGHUP signal * Handle SIGHUP signal
##### 2022/08/22 #### 2022/08/22
* Add strategy setting for each [DNS server](/configuration/dns/server) * Add strategy setting for each [DNS server](/configuration/dns/server)
* Add bind address to outbound options * Add bind address to outbound options
##### 2022/08/21 #### 2022/08/21
* Add [Tor outbound](/configuration/outbound/tor) * Add [Tor outbound](/configuration/outbound/tor)
* Add [SSH outbound](/configuration/outbound/ssh) * Add [SSH outbound](/configuration/outbound/ssh)
##### 2022/08/20 #### 2022/08/20
* Attempt to unwrap ip-in-fqdn socksaddr * Attempt to unwrap ip-in-fqdn socksaddr
* Fix read packages in android 12 * Fix read packages in android 12
@@ -189,53 +36,53 @@ and [Listen Fields](/configuration/shared/listen#udp_fragment).
* Skip bind connection with private destination to interface * Skip bind connection with private destination to interface
* Add [Trojan connection fallback](/configuration/inbound/trojan#fallback) * 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 Hysteria [Inbound](/configuration/inbound/hysteria) and [Outbund](/configuration/outbound/hysteria)
* Add [ACME TLS certificate issuer](/configuration/shared/tls) * Add [ACME TLS certificate issuer](/configuration/shared/tls)
* Allow read config from stdin (-c stdin) * Allow read config from stdin (-c stdin)
* Update gVisor to 20220815.0 * Update gVisor to 20220815.0
##### 2022/08/18 #### 2022/08/18
* Fix find process with lwip stack * Fix find process with lwip stack
* Fix crash on shadowsocks server * Fix crash on shadowsocks server
* Fix crash on darwin tun * Fix crash on darwin tun
* Fix write log to file * Fix write log to file
##### 2022/08/17 #### 2022/08/17
* Improve async dns transports * Improve async dns transports
##### 2022/08/16 #### 2022/08/16
* Add ip_version (route/dns) rule item * Add ip_version (route/dns) rule item
* Add [WireGuard](/configuration/outbound/wireguard) outbound * 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. * Add uid, android user and package rules support in [Tun](/configuration/inbound/tun) routing.
##### 2022/08/13 #### 2022/08/13
* Fix dns concurrent write * Fix dns concurrent write
##### 2022/08/12 #### 2022/08/12
* Performance improvements * Performance improvements
* Add UoT option for [SOCKS](/configuration/outbound/socks) outbound * 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 * 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 * Add full-featured [Naive](/configuration/inbound/naive) inbound
* Fix default dns server option [#9] by iKirby * Fix default dns server option [#9] by iKirby
##### 2022/08/09 #### 2022/08/09
No changelog before. No changelog before.
[#9]: https://github.com/SagerNet/sing-box/pull/9 [#9]: https://github.com/SagerNet/sing-box/pull/9

View File

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

View File

@@ -38,8 +38,7 @@
"private" "private"
], ],
"source_ip_cidr": [ "source_ip_cidr": [
"10.0.0.0/24", "10.0.0.0/24"
"192.168.0.1"
], ],
"source_port": [ "source_port": [
12345 12345
@@ -61,9 +60,6 @@
"process_name": [ "process_name": [
"curl" "curl"
], ],
"process_path": [
"/usr/bin/curl"
],
"package_name": [ "package_name": [
"com.termux" "com.termux"
], ],
@@ -73,7 +69,6 @@
"user_id": [ "user_id": [
1000 1000
], ],
"clash_mode": "direct",
"invert": false, "invert": false,
"outbound": [ "outbound": [
"direct" "direct"
@@ -181,14 +176,6 @@ Match port range.
Match process name. Match process name.
#### process_path
!!! error ""
Only supported on Linux, Windows, and macOS.
Match process path.
#### package_name #### package_name
Match android package name. Match android package name.
@@ -209,10 +196,6 @@ Match user name.
Match user id. Match user id.
#### clash_mode
Match Clash mode.
#### invert #### invert
Invert match result. Invert match result.

View File

@@ -60,9 +60,6 @@
"process_name": [ "process_name": [
"curl" "curl"
], ],
"process_path": [
"/usr/bin/curl"
],
"package_name": [ "package_name": [
"com.termux" "com.termux"
], ],
@@ -72,7 +69,6 @@
"user_id": [ "user_id": [
1000 1000
], ],
"clash_mode": "direct",
"invert": false, "invert": false,
"outbound": [ "outbound": [
"direct" "direct"
@@ -180,14 +176,6 @@
匹配进程名称。 匹配进程名称。
#### process_path
!!! error ""
仅支持 Linux、Windows 和 macOS.
匹配进程路径。
#### package_name #### package_name
匹配 Android 应用包名。 匹配 Android 应用包名。
@@ -208,10 +196,6 @@
匹配用户 ID。 匹配用户 ID。
#### clash_mode
匹配 Clash 模式。
#### invert #### invert
反选匹配结果。 反选匹配结果。

View File

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

View File

@@ -1,5 +1,3 @@
# Experimental
### Structure ### Structure
```json ```json
@@ -8,10 +6,7 @@
"clash_api": { "clash_api": {
"external_controller": "127.0.0.1:9090", "external_controller": "127.0.0.1:9090",
"external_ui": "folder", "external_ui": "folder",
"secret": "", "secret": ""
"default_mode": "rule",
"store_selected": false,
"cache_file": "cache.db"
} }
} }
} }
@@ -29,7 +24,7 @@
#### external_controller #### external_controller
RESTful web API listening address. Clash API will be disabled if empty. RESTful web API listening address. Disabled if empty.
#### external_ui #### external_ui
@@ -41,22 +36,4 @@ serve it at `http://{{external-controller}}/ui`.
Secret for the RESTful API (optional) Secret for the RESTful API (optional)
Authenticate by spedifying HTTP header `Authorization: Bearer ${secret}` Authenticate by spedifying HTTP header `Authorization: Bearer ${secret}`
ALWAYS set a secret if RESTful API is listening on 0.0.0.0 ALWAYS set a secret if RESTful API is listening on 0.0.0.0
#### default_mode
Default mode in clash, `rule` will be used if empty.
This setting has no direct effect, but can be used in routing and DNS rules via the `clash_mode` rule item.
#### store_selected
!!! note ""
The tag must be set for target outbounds.
Store selected outbound for the `Selector` outbound in cache file.
#### cache_file
Cache file path, `cache.db` will be used if empty.

View File

@@ -8,10 +8,7 @@
"clash_api": { "clash_api": {
"external_controller": "127.0.0.1:9090", "external_controller": "127.0.0.1:9090",
"external_ui": "folder", "external_ui": "folder",
"secret": "", "secret": ""
"default_mode": "rule",
"store_selected": false,
"cache_file": "cache.db"
} }
} }
} }
@@ -21,7 +18,7 @@
!!! error "" !!! error ""
默认安装不包含 Clash API参阅 [安装](/zh/#_2)。 默认安装不包含 Clash API参阅 [安装](/zh/#installation)。
!!! note "" !!! note ""
@@ -29,7 +26,7 @@
#### external_controller #### external_controller
RESTful web API 监听地址。如果为空,则禁用 Clash API。 RESTful web API 监听地址。
#### external_ui #### external_ui
@@ -39,22 +36,4 @@ RESTful web API 监听地址。如果为空,则禁用 Clash API。
RESTful API 的密钥(可选) RESTful API 的密钥(可选)
通过指定 HTTP 标头 `Authorization: Bearer ${secret}` 进行身份验证 通过指定 HTTP 标头 `Authorization: Bearer ${secret}` 进行身份验证
如果 RESTful API 正在监听 0.0.0.0,请始终设置一个密钥。 如果 RESTful API 正在监听 0.0.0.0,请始终设置一个密钥。
#### default_mode
Clash 中的默认模式,默认使用 `rule`
此设置没有直接影响,但可以通过 `clash_mode` 规则项在路由和 DNS 规则中使用。
#### store_selected
!!! note ""
必须为目标出站设置标签。
`Selector` 中出站的选定的目标出站存储在缓存文件中。
#### cache_file
缓存文件路径,默认使用`cache.db`

View File

@@ -4,22 +4,29 @@
```json ```json
{ {
"type": "direct", "inbounds": [
"tag": "direct-in", {
"type": "direct",
... // Listen Fields "tag": "direct-in",
"listen": "::",
"listen_port": 5353,
"tcp_fast_open": false,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"udp_timeout": 300,
"proxy_protocol": false,
"network": "udp", "network": "udp",
"override_address": "1.0.0.1", "override_address": "1.0.0.1",
"override_port": 53 "override_port": 53
}
]
} }
``` ```
### Listen Fields ### Direct Fields
See [Listen Fields](/configuration/shared/listen) for details.
### Fields
#### network #### network
@@ -33,4 +40,50 @@ Override the connection destination address.
#### override_port #### override_port
Override the connection destination port. Override the connection destination port.
### Listen Fields
#### listen
==Required==
Listen address.
#### listen_port
==Required==
Listen port.
#### tcp_fast_open
Enable tcp fast open for listener.
#### sniff
Enable sniffing.
See [Protocol Sniff](/configuration/route/sniff/) for details.
#### sniff_override_destination
Override the connection destination address with the sniffed domain.
If the domain name is invalid (like tor), this will not work.
#### domain_strategy
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
If set, the requested domain name will be resolved to IP before routing.
If `sniff_override_destination` is in effect, its value will be taken as a fallback.
#### udp_timeout
UDP NAT expiration time in seconds, default is 300 (5 minutes).
#### proxy_protocol
Parse [Proxy Protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) in the connection header.

View File

@@ -4,22 +4,29 @@
```json ```json
{ {
"type": "direct", "inbounds": [
"tag": "direct-in", {
"type": "direct",
"tag": "direct-in",
... // 监听字段 "listen": "::",
"listen_port": 5353,
"network": "udp", "tcp_fast_open": false,
"override_address": "1.0.0.1", "sniff": false,
"override_port": 53 "sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"udp_timeout": 300,
"network": "udp",
"proxy_protocol": false,
"override_address": "1.0.0.1",
"override_port": 53
}
]
} }
``` ```
### 监听字段 ### Direct 字段
参阅 [监听字段](/zh/configuration/shared/listen/)。
### 字段
#### network #### network
@@ -35,3 +42,48 @@
覆盖连接目标端口。 覆盖连接目标端口。
### 监听字段
#### listen
==必填==
监听地址。
#### listen_port
==必填==
监听端口。
#### tcp_fast_open
为监听器启用 TCP 快速打开。
#### sniff
启用协议探测。
参阅 [协议探测](/zh/configuration/route/sniff/)
#### sniff_override_destination
用探测出的域名覆盖连接目标地址。
如果域名无效(如 Tor将不生效。
#### domain_strategy
可选值: `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`
如果设置,请求的域名将在路由之前解析为 IP。
如果 `sniff_override_destination` 生效,它的值将作为后备。
#### udp_timeout
UDP NAT 过期时间,以秒为单位,默认为 3005 分钟)。
#### proxy_protocol
解析连接头中的 [代理协议](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)。

View File

@@ -2,27 +2,33 @@
```json ```json
{ {
"type": "http", "inbounds": [
"tag": "http-in",
... // Listen Fields
"users": [
{ {
"username": "admin", "type": "http",
"password": "admin" "tag": "http-in",
"listen": "::",
"listen_port": 2080,
"tcp_fast_open": false,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"proxy_protocol": false,
"users": [
{
"username": "admin",
"password": "admin"
}
],
"tls": {},
"set_system_proxy": false
} }
], ]
"tls": {},
"set_system_proxy": false
} }
``` ```
### Listen Fields ### HTTP Fields
See [Listen Fields](/configuration/shared/listen) for details.
### Fields
#### tls #### tls
@@ -41,3 +47,45 @@ No authentication required if empty.
Only supported on Linux, Android, Windows, and macOS. Only supported on Linux, Android, Windows, and macOS.
Automatically set system proxy configuration when start and clean up when stop. Automatically set system proxy configuration when start and clean up when stop.
### Listen Fields
#### listen
==Required==
Listen address.
#### listen_port
==Required==
Listen port.
#### tcp_fast_open
Enable tcp fast open for listener.
#### sniff
Enable sniffing.
See [Protocol Sniff](/configuration/route/sniff/) for details.
#### sniff_override_destination
Override the connection destination address with the sniffed domain.
If the domain name is invalid (like tor), this will not work.
#### domain_strategy
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
If set, the requested domain name will be resolved to IP before routing.
If `sniff_override_destination` is in effect, its value will be taken as a fallback.
#### proxy_protocol
Parse [Proxy Protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) in the connection header.

View File

@@ -2,27 +2,33 @@
```json ```json
{ {
"type": "http", "inbounds": [
"tag": "http-in",
... // 监听字段
"users": [
{ {
"username": "admin", "type": "http",
"password": "admin" "tag": "http-in",
"listen": "::",
"listen_port": 2080,
"tcp_fast_open": false,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"proxy_protocol": false,
"users": [
{
"username": "admin",
"password": "admin"
}
],
"tls": {},
"set_system_proxy": false
} }
], ]
"tls": {},
"set_system_proxy": false
} }
``` ```
### 监听字段 ### HTTP 字段
参阅 [监听字段](/zh/configuration/shared/listen/)。
### 字段
#### tls #### tls
@@ -32,7 +38,7 @@ TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
HTTP 用户 HTTP 用户
如果为空则不需要验证。 默认不需要验证。
#### set_system_proxy #### set_system_proxy
@@ -40,4 +46,46 @@ HTTP 用户
仅支持 Linux、Android、Windows 和 macOS。 仅支持 Linux、Android、Windows 和 macOS。
启动时自动设置系统代理,停止时自动清理。 启动时自动设置系统代理,停止时自动清理。
### 监听字段
#### listen
==必填==
监听地址。
#### listen_port
==必填==
监听端口。
#### tcp_fast_open
为监听器启用 TCP 快速打开。
#### sniff
启用协议探测。
参阅 [协议探测](/zh/configuration/route/sniff/)
#### sniff_override_destination
用探测出的域名覆盖连接目标地址。
如果域名无效(如 Tor将不生效。
#### domain_strategy
可选值: `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`
如果设置,请求的域名将在路由之前解析为 IP。
如果 `sniff_override_destination` 生效,它的值将作为后备。
#### proxy_protocol
解析连接头中的 [代理协议](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)。

View File

@@ -2,23 +2,31 @@
```json ```json
{ {
"type": "hysteria", "inbounds": [
"tag": "hysteria-in", {
"type": "hysteria",
... // Listen Fields "tag": "hysteria-in",
"up": "100 Mbps", "listen": "::",
"up_mbps": 100, "listen_port": 443,
"down": "100 Mbps", "sniff": false,
"down_mbps": 100, "sniff_override_destination": false,
"obfs": "fuck me till the daylight", "domain_strategy": "prefer_ipv6",
"auth": "",
"auth_str": "password", "up": "100 Mbps",
"recv_window_conn": 0, "up_mbps": 100,
"recv_window_client": 0, "down": "100 Mbps",
"max_conn_client": 0, "down_mbps": 100,
"disable_mtu_discovery": false, "obfs": "fuck me till the daylight",
"tls": {} "auth": "",
"auth_str": "password",
"recv_window_conn": 0,
"recv_window_client": 0,
"max_conn_client": 0,
"disable_mtu_discovery": false,
"tls": {}
}
]
} }
``` ```
@@ -26,11 +34,7 @@
QUIC, which is required by hysteria is not included by default, see [Installation](/#installation). QUIC, which is required by hysteria is not included by default, see [Installation](/#installation).
### Listen Fields ### Hysteria Fields
See [Listen Fields](/configuration/shared/listen) for details.
### Fields
#### up, down #### up, down
@@ -97,4 +101,38 @@ Force enabled on for systems other than Linux and Windows (according to upstream
==Required== ==Required==
TLS configuration, see [TLS](/configuration/shared/tls/#inbound). TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
### Listen Fields
#### listen
==Required==
Listen address.
#### listen_port
==Required==
Listen port.
#### sniff
Enable sniffing.
See [Protocol Sniff](/configuration/route/sniff/) for details.
#### sniff_override_destination
Override the connection destination address with the sniffed domain.
If the domain name is invalid (like tor), this will not work.
#### domain_strategy
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
If set, the requested domain name will be resolved to IP before routing.
If `sniff_override_destination` is in effect, its value will be taken as a fallback.

View File

@@ -2,35 +2,39 @@
```json ```json
{ {
"type": "hysteria", "inbounds": [
"tag": "hysteria-in", {
"type": "hysteria",
... // 监听字段 "tag": "hysteria-in",
"up": "100 Mbps", "listen": "::",
"up_mbps": 100, "listen_port": 443,
"down": "100 Mbps", "sniff": false,
"down_mbps": 100, "sniff_override_destination": false,
"obfs": "fuck me till the daylight", "domain_strategy": "prefer_ipv6",
"auth": "",
"auth_str": "password", "up": "100 Mbps",
"recv_window_conn": 0, "up_mbps": 100,
"recv_window_client": 0, "down": "100 Mbps",
"max_conn_client": 0, "down_mbps": 100,
"disable_mtu_discovery": false, "obfs": "fuck me till the daylight",
"tls": {} "auth": "",
"auth_str": "password",
"recv_window_conn": 0,
"recv_window_client": 0,
"max_conn_client": 0,
"disable_mtu_discovery": false,
"tls": {}
}
]
} }
``` ```
!!! warning "" !!! warning ""
默认安装不包含被 Hysteria 依赖的 QUIC参阅 [安装](/zh/#_2)。 默认安装不包含被 Hysteria 依赖的 QUIC参阅 [安装](/zh/#installation)。
### 监听字段 ### Hysteria 字段
参阅 [监听字段](/zh/configuration/shared/listen/)。
### 字段
#### up, down #### up, down
@@ -97,4 +101,38 @@ base64 编码的认证密码。
==必填== ==必填==
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。 TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
### 监听字段
#### listen
==必填==
监听地址。
#### listen_port
==必填==
监听端口。
#### sniff
启用协议探测。
参阅 [协议探测](/zh/configuration/route/sniff/)。
#### sniff_override_destination
用探测出的域名覆盖连接目标地址。
如果域名无效(如 Tor将不生效。
#### domain_strategy
可选值: `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`
如果设置,请求的域名将在路由之前解析为 IP。
如果 `sniff_override_destination` 生效,它的值将作为后备。

View File

@@ -1,5 +1,3 @@
# Inbound
### Structure ### Structure
```json ```json
@@ -15,20 +13,20 @@
### Fields ### Fields
| Type | Format | Injectable | | Type | Format |
|---------------|------------------------------|------------| |---------------|------------------------------|
| `direct` | [Direct](./direct) | X | | `direct` | [Direct](./direct) |
| `mixed` | [Mixed](./mixed) | TCP | | `mixed` | [Mixed](./mixed) |
| `socks` | [SOCKS](./socks) | TCP | | `socks` | [SOCKS](./socks) |
| `http` | [HTTP](./http) | TCP | | `http` | [HTTP](./http) |
| `shadowsocks` | [Shadowsocks](./shadowsocks) | TCP | | `shadowsocks` | [Shadowsocks](./shadowsocks) |
| `vmess` | [VMess](./vmess) | TCP | | `vmess` | [VMess](./vmess) |
| `trojan` | [Trojan](./trojan) | TCP | | `trojan` | [Trojan](./trojan) |
| `naive` | [Naive](./naive) | X | | `naive` | [Naive](./naive) |
| `hysteria` | [Hysteria](./hysteria) | X | | `hysteria` | [Hysteria](./hysteria) |
| `tun` | [Tun](./tun) | X | | `tun` | [Tun](./tun) |
| `redirect` | [Redirect](./redirect) | X | | `redirect` | [Redirect](./redirect) |
| `tproxy` | [TProxy](./tproxy) | X | | `tproxy` | [TProxy](./tproxy) |
#### tag #### tag

View File

@@ -15,20 +15,20 @@
### 字段 ### 字段
| 类型 | 格式 | 注入支持 | | 类型 | 格式 |
|---------------|------------------------------|------| |---------------|------------------------------|
| `direct` | [Direct](./direct) | X | | `direct` | [Direct](./direct) |
| `mixed` | [Mixed](./mixed) | TCP | | `mixed` | [Mixed](./mixed) |
| `socks` | [SOCKS](./socks) | TCP | | `socks` | [SOCKS](./socks) |
| `http` | [HTTP](./http) | TCP | | `http` | [HTTP](./http) |
| `shadowsocks` | [Shadowsocks](./shadowsocks) | TCP | | `shadowsocks` | [Shadowsocks](./shadowsocks) |
| `vmess` | [VMess](./vmess) | TCP | | `vmess` | [VMess](./vmess) |
| `trojan` | [Trojan](./trojan) | TCP | | `trojan` | [Trojan](./trojan) |
| `naive` | [Naive](./naive) | X | | `naive` | [Naive](./naive) |
| `hysteria` | [Hysteria](./hysteria) | X | | `hysteria` | [Hysteria](./hysteria) |
| `tun` | [Tun](./tun) | X | | `tun` | [Tun](./tun) |
| `redirect` | [Redirect](./redirect) | X | | `redirect` | [Redirect](./redirect) |
| `tproxy` | [TProxy](./tproxy) | X | | `tproxy` | [TProxy](./tproxy) |
#### tag #### tag

View File

@@ -4,26 +4,32 @@
```json ```json
{ {
"type": "mixed", "inbounds": [
"tag": "mixed-in",
... // Listen Fields
"users": [
{ {
"username": "admin", "type": "mixed",
"password": "admin" "tag": "mixed-in",
"listen": "::",
"listen_port": 2080,
"tcp_fast_open": false,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"proxy_protocol": false,
"users": [
{
"username": "admin",
"password": "admin"
}
],
"set_system_proxy": false
} }
], ]
"set_system_proxy": false
} }
``` ```
### Listen Fields ### Mixed Fields
See [Listen Fields](/configuration/shared/listen) for details.
### Fields
#### users #### users
@@ -33,6 +39,52 @@ No authentication required if empty.
#### set_system_proxy #### set_system_proxy
!!! error ""
Only supported on Linux, Android, Windows, and macOS.
Automatically set system proxy configuration when start and clean up when stop.
### Listen Fields
#### listen
==Required==
Listen address.
#### listen_port
==Required==
Listen port.
#### tcp_fast_open
Enable tcp fast open for listener.
#### sniff
Enable sniffing.
See [Protocol Sniff](/configuration/route/sniff/) for details.
#### sniff_override_destination
Override the connection destination address with the sniffed domain.
If the domain name is invalid (like tor), this will not work.
#### domain_strategy
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
If set, the requested domain name will be resolved to IP before routing.
If `sniff_override_destination` is in effect, its value will be taken as a fallback.
#### set_system_proxy
!!! error "" !!! error ""
Only supported on Linux, Android, Windows, and macOS. Only supported on Linux, Android, Windows, and macOS.

View File

@@ -4,32 +4,38 @@
```json ```json
{ {
"type": "mixed", "inbounds": [
"tag": "mixed-in",
... // 监听字段
"users": [
{ {
"username": "admin", "type": "mixed",
"password": "admin" "tag": "mixed-in",
"listen": "::",
"listen_port": 2080,
"tcp_fast_open": false,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"proxy_protocol": false,
"users": [
{
"username": "admin",
"password": "admin"
}
],
"set_system_proxy": false
} }
], ]
"set_system_proxy": false
} }
``` ```
### 监听字段 ### Mixed 字段
参阅 [监听字段](/zh/configuration/shared/listen/)。
### 字段
#### users #### users
SOCKS 和 HTTP 用户 SOCKS 和 HTTP 用户
如果为空则不需要验证。 默认不需要验证。
#### set_system_proxy #### set_system_proxy
@@ -37,4 +43,46 @@ SOCKS 和 HTTP 用户
仅支持 Linux、Android、Windows 和 macOS。 仅支持 Linux、Android、Windows 和 macOS。
启动时自动设置系统代理,停止时自动清理。 启动时自动设置系统代理,停止时自动清理。
### 监听字段
#### listen
==必填==
监听地址。
#### listen_port
==必填==
监听端口。
#### tcp_fast_open
为监听器启用 TCP 快速打开。
#### sniff
启用协议探测。
参阅 [协议探测](/zh/configuration/route/sniff/)。
#### sniff_override_destination
用探测出的域名覆盖连接目标地址。
如果域名无效(如 Tor将不生效。
#### domain_strategy
可选值: `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`
如果设置,请求的域名将在路由之前解析为 IP。
如果 `sniff_override_destination` 生效,它的值将作为后备。
#### proxy_protocol
解析连接头中的 [代理协议](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)。

View File

@@ -2,19 +2,29 @@
```json ```json
{ {
"type": "naive", "inbounds": [
"tag": "naive-in",
"network": "udp",
... // Listen Fields
"users": [
{ {
"username": "sekai", "type": "naive",
"password": "password" "tag": "naive-in",
"listen": "::",
"listen_port": 443,
"tcp_fast_open": false,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"proxy_protocol": false,
"network": "udp",
"users": [
{
"username": "sekai",
"password": "password"
}
],
"tls": {}
} }
], ]
"tls": {}
} }
``` ```
@@ -22,11 +32,7 @@
HTTP3 transport is not included by default, see [Installation](/#installation). HTTP3 transport is not included by default, see [Installation](/#installation).
### Listen Fields ### Naive Fields
See [Listen Fields](/configuration/shared/listen) for details.
### Fields
#### network #### network
@@ -42,4 +48,46 @@ Naive users.
#### tls #### tls
TLS configuration, see [TLS](/configuration/shared/tls/#inbound). TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
### Listen Fields
#### listen
==Required==
Listen address.
#### listen_port
==Required==
Listen port.
#### tcp_fast_open
Enable tcp fast open for listener.
#### sniff
Enable sniffing.
See [Protocol Sniff](/configuration/route/sniff/) for details.
#### sniff_override_destination
Override the connection destination address with the sniffed domain.
If the domain name is invalid (like tor), this will not work.
#### domain_strategy
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
If set, the requested domain name will be resolved to IP before routing.
If `sniff_override_destination` is in effect, its value will be taken as a fallback.
#### proxy_protocol
Parse [Proxy Protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) in the connection header.

View File

@@ -2,31 +2,37 @@
```json ```json
{ {
"type": "naive", "inbounds": [
"tag": "naive-in",
"network": "udp",
... // 监听字段
"users": [
{ {
"username": "sekai", "type": "naive",
"password": "password" "tag": "naive-in",
"listen": "::",
"listen_port": 443,
"tcp_fast_open": false,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"proxy_protocol": false,
"network": "udp",
"users": [
{
"username": "sekai",
"password": "password"
}
],
"tls": {}
} }
], ]
"tls": {}
} }
``` ```
!!! warning "" !!! warning ""
默认安装不包含 HTTP3 传输层, 参阅 [安装](/zh/#_2)。 默认安装不包含 HTTP3 传输层, 参阅 [安装](/zh/#installation)。
### 监听字段 ### Naive 字段
参阅 [监听字段](/zh/configuration/shared/listen/)。
### 字段
#### network #### network
@@ -42,4 +48,46 @@ Naive 用户。
#### tls #### tls
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。 TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
### 监听字段
#### listen
==必填==
监听地址。
#### listen_port
==必填==
监听端口。
#### tcp_fast_open
为监听器启用 TCP 快速打开。
#### sniff
启用协议探测。
参阅 [协议探测](/zh/configuration/route/sniff/)。
#### sniff_override_destination
用探测出的域名覆盖连接目标地址。
如果域名无效(如 Tor将不生效。
#### domain_strategy
可选值: `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`
如果设置,请求的域名将在路由之前解析为 IP。
如果 `sniff_override_destination` 生效,它的值将作为后备。
#### proxy_protocol
解析连接头中的 [代理协议](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)。

View File

@@ -1,18 +1,52 @@
!!! error ""
Only supported on Linux and macOS.
### Structure ### Structure
```json ```json
{ {
"type": "redirect", "inbounds": [
"tag": "redirect-in", {
"type": "redirect",
... // Listen Fields "tag": "redirect-in",
"listen": "::",
"listen_port": 5353,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6"
}
]
} }
``` ```
### Listen Fields ### Listen Fields
See [Listen Fields](/configuration/shared/listen) for details. #### listen
==Required==
Listen address.
#### listen_port
==Required==
Listen port.
#### sniff
Enable sniffing.
See [Protocol Sniff](/configuration/route/sniff/) for details.
#### sniff_override_destination
Override the connection destination address with the sniffed domain.
If the domain name is invalid (like tor), this will not work.
#### domain_strategy
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
If set, the requested domain name will be resolved to IP before routing.
If `sniff_override_destination` is in effect, its value will be taken as a fallback.

View File

@@ -1,17 +1,52 @@
!!! error ""
仅支持 Linux 和 macOS。
### 结构 ### 结构
```json ```json
{ {
"type": "redirect", "inbounds": [
"tag": "redirect-in", {
"type": "redirect",
... // 监听字段 "tag": "redirect-in",
"listen": "::",
"listen_port": 5353,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6"
}
]
} }
``` ```
### 监听字段 ### 监听字段
参阅 [监听字段](/zh/configuration/shared/listen/)。 #### listen
==必填==
监听地址。
#### listen_port
==必填==
监听端口。
#### sniff
启用协议探测。
参阅 [协议探测](/zh/configuration/route/sniff/)。
#### sniff_override_destination
用探测出的域名覆盖连接目标地址。
如果域名无效(如 Tor将不生效。
#### domain_strategy
可选值: `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`
如果设置,请求的域名将在路由之前解析为 IP。
如果 `sniff_override_destination` 生效,它的值将作为后备。

View File

@@ -2,13 +2,25 @@
```json ```json
{ {
"type": "shadowsocks", "inbounds": [
"tag": "ss-in", {
"type": "shadowsocks",
... // Listen Fields "tag": "ss-in",
"method": "2022-blake3-aes-128-gcm", "listen": "::",
"password": "8JCsPssfgS8tiRwiMlhARg==" "listen_port": 5353,
"tcp_fast_open": false,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"udp_timeout": 300,
"network": "udp",
"proxy_protocol": false,
"method": "2022-blake3-aes-128-gcm",
"password": "8JCsPssfgS8tiRwiMlhARg=="
}
]
} }
``` ```
@@ -16,12 +28,17 @@
```json ```json
{ {
"method": "2022-blake3-aes-128-gcm", "inbounds": [
"password": "8JCsPssfgS8tiRwiMlhARg==",
"users": [
{ {
"name": "sekai", "type": "shadowsocks",
"password": "PCD2Z4o12bKUoFa3cC97Hw==" "method": "2022-blake3-aes-128-gcm",
"password": "8JCsPssfgS8tiRwiMlhARg==",
"users": [
{
"name": "sekai",
"password": "PCD2Z4o12bKUoFa3cC97Hw=="
}
]
} }
] ]
} }
@@ -31,25 +48,25 @@
```json ```json
{ {
"type": "shadowsocks", "inbounds": [
"method": "2022-blake3-aes-128-gcm",
"password": "8JCsPssfgS8tiRwiMlhARg==",
"destinations": [
{ {
"name": "test", "type": "shadowsocks",
"server": "example.com", "method": "2022-blake3-aes-128-gcm",
"server_port": 8080, "password": "8JCsPssfgS8tiRwiMlhARg==",
"password": "PCD2Z4o12bKUoFa3cC97Hw==" "destinations": [
{
"name": "test",
"server": "example.com",
"server_port": 8080,
"password": "PCD2Z4o12bKUoFa3cC97Hw=="
}
]
} }
] ]
} }
``` ```
### Listen Fields ### Shadowsocks Fields
See [Listen Fields](/configuration/shared/listen) for details.
### Fields
#### network #### network

View File

@@ -2,13 +2,25 @@
```json ```json
{ {
"type": "shadowsocks", "inbounds": [
"tag": "ss-in", {
"type": "shadowsocks",
... // 监听字段 "tag": "ss-in",
"method": "2022-blake3-aes-128-gcm", "listen": "::",
"password": "8JCsPssfgS8tiRwiMlhARg==" "listen_port": 5353,
"tcp_fast_open": false,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"udp_timeout": 300,
"network": "udp",
"proxy_protocol": false,
"method": "2022-blake3-aes-128-gcm",
"password": "8JCsPssfgS8tiRwiMlhARg=="
}
]
} }
``` ```
@@ -16,12 +28,17 @@
```json ```json
{ {
"method": "2022-blake3-aes-128-gcm", "inbounds": [
"password": "8JCsPssfgS8tiRwiMlhARg==",
"users": [
{ {
"name": "sekai", "type": "shadowsocks",
"password": "PCD2Z4o12bKUoFa3cC97Hw==" "method": "2022-blake3-aes-128-gcm",
"password": "8JCsPssfgS8tiRwiMlhARg==",
"users": [
{
"name": "sekai",
"password": "PCD2Z4o12bKUoFa3cC97Hw=="
}
]
} }
] ]
} }
@@ -31,25 +48,25 @@
```json ```json
{ {
"type": "shadowsocks", "inbounds": [
"method": "2022-blake3-aes-128-gcm",
"password": "8JCsPssfgS8tiRwiMlhARg==",
"destinations": [
{ {
"name": "test", "type": "shadowsocks",
"server": "example.com", "method": "2022-blake3-aes-128-gcm",
"server_port": 8080, "password": "8JCsPssfgS8tiRwiMlhARg==",
"password": "PCD2Z4o12bKUoFa3cC97Hw==" "destinations": [
{
"name": "test",
"server": "example.com",
"server_port": 8080,
"password": "PCD2Z4o12bKUoFa3cC97Hw=="
}
]
} }
] ]
} }
``` ```
### Listen Fields ### Shadowsocks 字段
See [Listen Fields](/configuration/shared/listen) for details.
### 字段
#### network #### network
@@ -81,4 +98,50 @@ See [Listen Fields](/configuration/shared/listen) for details.
|---------------|-------------------------------| |---------------|-------------------------------|
| none | / | | none | / |
| 2022 methods | `openssl rand -base64 <密钥长度>` | | 2022 methods | `openssl rand -base64 <密钥长度>` |
| other methods | 任意字符串 | | other methods | 任意字符串 |
### 监听字段
#### listen
==必填==
监听地址。
#### listen_port
==必填==
监听端口。
#### tcp_fast_open
为监听器启用 TCP 快速打开。
#### sniff
启用协议探测。
参阅 [协议探测](/zh/configuration/route/sniff/)。
#### sniff_override_destination
用探测出的域名覆盖连接目标地址。
如果域名无效(如 Tor将不生效。
#### domain_strategy
可选值: `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`
如果设置,请求的域名将在路由之前解析为 IP。
如果 `sniff_override_destination` 生效,它的值将作为后备。
#### udp_timeout
UDP NAT 过期时间,以秒为单位,默认为 3005 分钟)。
#### proxy_protocol
解析连接头中的 [代理协议](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)。

View File

@@ -1,31 +0,0 @@
### Structure
```json
{
"type": "shadowtls",
"tag": "st-in",
... // Listen Fields
"handshake": {
"server": "google.com",
"server_port": 443,
... // Dial Fields
}
}
```
### Listen Fields
See [Listen Fields](/configuration/shared/listen) for details.
### Fields
#### handshake
==Required==
Handshake server address and [dial options](/configuration/shared/dial).

View File

@@ -1,29 +0,0 @@
### 结构
```json
{
"type": "shadowtls",
"tag": "st-in",
... // 监听字段
"handshake": {
"server": "google.com",
"server_port": 443,
... // 拨号字段
}
}
```
### 监听字段
参阅 [监听字段](/zh/configuration/shared/listen/)。
### 字段
#### handshake
==必填==
握手服务器地址和 [拨号参数](/zh/configuration/shared/dial/)。

View File

@@ -4,28 +4,76 @@
```json ```json
{ {
"type": "socks", "inbounds": [
"tag": "socks-in",
... // Listen Fields
"users": [
{ {
"username": "admin", "type": "socks",
"password": "admin" "tag": "socks-in",
"listen": "::",
"listen_port": 2080,
"tcp_fast_open": false,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"proxy_protocol": false,
"users": [
{
"username": "admin",
"password": "admin"
}
]
} }
] ]
} }
``` ```
### Listen Fields ### SOCKS Fields
See [Listen Fields](/configuration/shared/listen) for details.
### Fields
#### users #### users
SOCKS users. SOCKS users.
No authentication required if empty. No authentication required if empty.
### 监听字段
#### listen
==必填==
监听地址
#### listen_port
==必填==
监听端口
#### tcp_fast_open
为监听器启用 TCP 快速打开
#### sniff
启用协议探测。
参阅 [协议探测](/zh/configuration/route/sniff/)
#### sniff_override_destination
用探测出的域名覆盖连接目标地址。
如果域名无效(如 Tor将不生效。
#### domain_strategy
可选值: `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
如果设置,请求的域名将在路由之前解析为 IP。
如果 `sniff_override_destination` 生效,它的值将作为后备。
#### proxy_protocol
解析连接头中的 [代理协议](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)。

View File

@@ -4,28 +4,76 @@
```json ```json
{ {
"type": "socks", "inbounds": [
"tag": "socks-in",
... // 监听字段
"users": [
{ {
"username": "admin", "type": "socks",
"password": "admin" "tag": "socks-in",
"listen": "::",
"listen_port": 2080,
"tcp_fast_open": false,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"proxy_protocol": false,
"users": [
{
"username": "admin",
"password": "admin"
}
]
} }
] ]
} }
``` ```
### 监听字段 ### SOCKS 字段
参阅 [监听字段](/zh/configuration/shared/listen/)。
### 字段
#### users #### users
SOCKS 用户 SOCKS 用户
如果为空则不需要验证。 默认不需要验证。
### Listen Fields
#### listen
==必填==
监听地址。
#### listen_port
==必填==
监听端口。
#### tcp_fast_open
为监听器启用 TCP 快速打开。
#### sniff
启用协议探测。
参阅 [协议探测](/zh/configuration/route/sniff/)。
#### sniff_override_destination
用探测出的域名覆盖连接目标地址。
如果域名无效(如 Tor将不生效。
#### domain_strategy
可选值: `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`
如果设置,请求的域名将在路由之前解析为 IP。
如果 `sniff_override_destination` 生效,它的值将作为后备。
#### proxy_protocol
解析连接头中的 [代理协议](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)。

View File

@@ -1,28 +1,67 @@
!!! error ""
Only supported on Linux.
### Structure ### Structure
```json ```json
{ {
"type": "tproxy", "inbounds": [
"tag": "tproxy-in", {
"type": "tproxy",
... // Listen Fields "tag": "tproxy-in",
"network": "udp" "listen": "::",
"listen_port": 5353,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"udp_timeout": 300,
"network": "udp"
}
]
} }
``` ```
### Listen Fields ### TProxy Fields
See [Listen Fields](/configuration/shared/listen) for details.
### Fields
#### network #### network
Listen network, one of `tcp` `udp`. Listen network, one of `tcp` `udp`.
Both if empty. Both if empty.
### Listen Fields
#### listen
==Required==
Listen address.
#### listen_port
==Required==
Listen port.
#### sniff
Enable sniffing.
See [Protocol Sniff](/configuration/route/sniff/) for details.
#### sniff_override_destination
Override the connection destination address with the sniffed domain.
If the domain name is invalid (like tor), this will not work.
#### domain_strategy
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
If set, the requested domain name will be resolved to IP before routing.
If `sniff_override_destination` is in effect, its value will be taken as a fallback.
#### udp_timeout
UDP NAT expiration time in seconds, default is 300 (5 minutes).

View File

@@ -1,28 +1,67 @@
!!! error ""
仅支持 Linux。
### 结构 ### 结构
```json ```json
{ {
"type": "tproxy", "inbounds": [
"tag": "tproxy-in", {
"type": "tproxy",
... // 监听字段 "tag": "tproxy-in",
"network": "udp" "listen": "::",
"listen_port": 5353,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"udp_timeout": 300,
"network": "udp"
}
]
} }
``` ```
### 监听字段 ### TProxy 字段
参阅 [监听字段](/zh/configuration/shared/listen/)。
### 字段
#### network #### network
监听的网络协议,`tcp` `udp` 之一。 监听的网络协议,`tcp` `udp` 之一。
默认所有。 默认所有。
### 监听字段
#### listen
==必填==
监听地址。
#### listen_port
==必填==
监听端口。
#### sniff
启用协议探测。
参阅 [协议探测](/zh/configuration/route/sniff/)。
#### sniff_override_destination
用探测出的域名覆盖连接目标地址。
如果域名无效(如 Tor将不生效。
#### domain_strategy
可选值: `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`
如果设置,请求的域名将在路由之前解析为 IP。
如果 `sniff_override_destination` 生效,它的值将作为后备。
#### udp_timeout
UDP NAT 过期时间,以秒为单位,默认为 3005 分钟)。

View File

@@ -2,37 +2,37 @@
```json ```json
{ {
"type": "trojan", "inbounds": [
"tag": "trojan-in",
... // Listen Fields
"users": [
{ {
"name": "sekai", "type": "trojan",
"password": "8JCsPssfgS8tiRwiMlhARg==" "tag": "trojan-in",
"listen": "::",
"listen_port": 2080,
"tcp_fast_open": false,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"proxy_protocol": false,
"users": [
{
"name": "sekai",
"password": "8JCsPssfgS8tiRwiMlhARg=="
}
],
"tls": {},
"fallback": {
"server": "127.0.0.0.1",
"server_port": 8080
},
"transport": {}
} }
], ]
"tls": {},
"fallback": {
"server": "127.0.0.1",
"server_port": 8080
},
"fallback_for_alpn": {
"http/1.1": {
"server": "127.0.0.1",
"server_port": 8081
}
},
"transport": {}
} }
``` ```
### Listen Fields ### Trojan Fields
See [Listen Fields](/configuration/shared/listen) for details.
### Fields
#### users #### users
@@ -50,14 +50,50 @@ 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. 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 `fallback` and `fallback_for_alpn` are empty. Fallback server configuration. Disabled if 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 #### transport
V2Ray Transport configuration, see [V2Ray Transport](/configuration/shared/v2ray-transport). V2Ray Transport configuration, see [V2Ray Transport](/configuration/shared/v2ray-transport).
### Listen Fields
#### listen
==Required==
Listen address.
#### listen_port
==Required==
Listen port.
#### tcp_fast_open
Enable tcp fast open for listener.
#### sniff
Enable sniffing.
See [Protocol Sniff](/configuration/route/sniff/) for details.
#### sniff_override_destination
Override the connection destination address with the sniffed domain.
If the domain name is invalid (like tor), this will not work.
#### domain_strategy
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
If set, the requested domain name will be resolved to IP before routing.
If `sniff_override_destination` is in effect, its value will be taken as a fallback.
#### proxy_protocol
Parse [Proxy Protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) in the connection header.

View File

@@ -2,37 +2,37 @@
```json ```json
{ {
"type": "trojan", "inbounds": [
"tag": "trojan-in",
... // 监听字段
"users": [
{ {
"name": "sekai", "type": "trojan",
"password": "8JCsPssfgS8tiRwiMlhARg==" "tag": "trojan-in",
"listen": "::",
"listen_port": 2080,
"tcp_fast_open": false,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"proxy_protocol": false,
"users": [
{
"name": "sekai",
"password": "8JCsPssfgS8tiRwiMlhARg=="
}
],
"tls": {},
"fallback": {
"server": "127.0.0.0.1",
"server_port": 8080
},
"transport": {}
} }
], ]
"tls": {},
"fallback": {
"server": "127.0.0.1",
"server_port": 8080
},
"fallback_for_alpn": {
"http/1.1": {
"server": "127.0.0.1",
"server_port": 8081
}
},
"transport": {}
} }
``` ```
### 监听字段 ### Trojan 字段
参阅 [监听字段](/zh/configuration/shared/listen/)。
### 字段
#### users #### users
@@ -50,16 +50,52 @@ TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
!!! error "" !!! error ""
没有证据表明 GFW 基于 HTTP 响应检测并阻止 Trojan 服务器,并且在服务器上打开标准 http/s 端口是一个更大的特征。 没有证据表明 GFW 基于 HTTP 响应检测并阻止木马服务器,并且在服务器上打开标准 http/s 端口是一个更大的特征。
回退服务器配置。如果 `fallback``fallback_for_alpn` 为空,则禁用回退 备用服务器配置。默认禁用
#### fallback_for_alpn
为 ALPN 指定回退服务器配置。
如果不为空ALPN 不在此列表中的 TLS 回退请求将被拒绝。
#### transport #### transport
V2Ray 传输配置,参阅 [V2Ray 传输层](/zh/configuration/shared/v2ray-transport)。 V2Ray 传输配置,参阅 [V2Ray 传输层](/zh/configuration/shared/v2ray-transport)。
### 监听字段
#### listen
==必填==
监听地址。
#### listen_port
==必填==
监听端口。
#### tcp_fast_open
为监听器启用 TCP 快速打开。
#### sniff
启用协议探测。
参阅 [协议探测](/zh/configuration/route/sniff/)。
#### sniff_override_destination
用探测出的域名覆盖连接目标地址。
如果域名无效(如 Tor将不生效。
#### domain_strategy
可选值: `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`
如果设置,请求的域名将在路由之前解析为 IP。
如果 `sniff_override_destination` 生效,它的值将作为后备。
#### proxy_protocol
解析连接头中的 [代理协议](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)。

View File

@@ -6,41 +6,47 @@
```json ```json
{ {
"type": "tun", "inbounds": [
"tag": "tun-in", {
"type": "tun",
"interface_name": "tun0", "tag": "tun-in",
"inet4_address": "172.19.0.1/30", "interface_name": "tun0",
"inet6_address": "fdfe:dcba:9876::1/126", "inet4_address": "172.19.0.1/30",
"mtu": 9000, "inet6_address": "fdfe:dcba:9876::1/128",
"auto_route": true, "mtu": 1500,
"strict_route": true, "auto_route": true,
"endpoint_independent_nat": false, "endpoint_independent_nat": false,
"stack": "gvisor", "udp_timeout": 300,
"include_uid": [ "stack": "gvisor",
0 "include_uid": [
], 0
"include_uid_range": [ ],
"1000-99999" "include_uid_range": [
], [
"exclude_uid": [ "1000-99999"
1000 ]
], ],
"exclude_uid_range": [ "exclude_uid": [
"1000-99999" 1000
], ],
"include_android_user": [ "exclude_uid_range": [
0, "1000-99999"
10 ],
], "include_android_user": [
"include_package": [ 0,
"com.android.chrome" 10
], ],
"exclude_package": [ "include_package": [
"com.android.captiveportallogin" "com.android.chrome"
], ],
"exclude_package": [
... // Listen Fields "com.android.captiveportallogin"
],
"sniff": true,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv4"
}
]
} }
``` ```
@@ -52,7 +58,7 @@
If tun is running in non-privileged mode, addresses and MTU will not be configured automatically, please make sure the settings are accurate. If tun is running in non-privileged mode, addresses and MTU will not be configured automatically, please make sure the settings are accurate.
### Fields ### Tun Fields
#### interface_name #### interface_name
@@ -80,26 +86,8 @@ Set the default route to the Tun.
To avoid traffic loopback, set `route.auto_detect_interface` or `route.default_interface` or `outbound.bind_interface` To avoid traffic loopback, set `route.auto_detect_interface` or `route.default_interface` or `outbound.bind_interface`
!!! note "Use with Android VPN"
By default, VPN takes precedence over tun. To make tun go through VPN, enable `route.override_android_vpn`.
#### strict_route
Enforce strict routing rules in Linux when `auto_route` is enabled:
* Let unsupported network unreachable
* Route all connections to tun
It prevents address leaks and makes DNS hijacking work on Android and Linux with systemd-resolved, but your device will
not be accessible by others.
#### endpoint_independent_nat #### endpoint_independent_nat
!!! info ""
This item is only available on the gvisor stack, other stacks are endpoint-independent NAT by default.
Enable endpoint-independent NAT. Enable endpoint-independent NAT.
Performance may degrade slightly, so it is not recommended to enable on when it is not needed. Performance may degrade slightly, so it is not recommended to enable on when it is not needed.
@@ -112,11 +100,10 @@ UDP NAT expiration time in seconds, default is 300 (5 minutes).
TCP/IP stack. TCP/IP stack.
| Stack | Description | Status | | Stack | Upstream | Status |
|------------------|--------------------------------------------------------------------------------|-------------------| |------------------|-----------------------------------------------------------------------|-------------------|
| gVisor (default) | Based on [google/gvisor](https://github.com/google/gvisor) | recommended | | gVisor (default) | [google/gvisor](https://github.com/google/gvisor) | recommended |
| system | Less compatibility and sometimes better performance. | recommended | | LWIP | [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived |
| LWIP | Based on [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived |
!!! warning "" !!! warning ""
@@ -165,4 +152,22 @@ Exclude android packages in route.
### Listen Fields ### Listen Fields
See [Listen Fields](/configuration/shared/listen) for details. #### sniff
Enable sniffing.
See [Protocol Sniff](/configuration/route/sniff/) for details.
#### sniff_override_destination
Override the connection destination address with the sniffed domain.
If the domain name is invalid (like tor), this will not work.
#### domain_strategy
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
If set, the requested domain name will be resolved to IP before routing.
If `sniff_override_destination` is in effect, its value will be taken as a fallback.

View File

@@ -6,41 +6,47 @@
```json ```json
{ {
"type": "tun", "inbounds": [
"tag": "tun-in", {
"type": "tun",
"interface_name": "tun0", "tag": "tun-in",
"inet4_address": "172.19.0.1/30", "interface_name": "tun0",
"inet6_address": "fdfe:dcba:9876::1/126", "inet4_address": "172.19.0.1/30",
"mtu": 9000, "inet6_address": "fdfe:dcba:9876::1/128",
"auto_route": true, "mtu": 1500,
"strict_route": true, "auto_route": true,
"endpoint_independent_nat": false, "endpoint_independent_nat": false,
"stack": "gvisor", "udp_timeout": 300,
"include_uid": [ "stack": "gvisor",
0 "include_uid": [
], 0
"include_uid_range": [ ],
"1000-99999" "include_uid_range": [
], [
"exclude_uid": [ "1000-99999"
1000 ]
], ],
"exclude_uid_range": [ "exclude_uid": [
"1000-99999" 1000
], ],
"include_android_user": [ "exclude_uid_range": [
0, "1000-99999"
10 ],
], "include_android_user": [
"include_package": [ 0,
"com.android.chrome" 10
], ],
"exclude_package": [ "include_package": [
"com.android.captiveportallogin" "com.android.chrome"
], ],
"exclude_package": [
... // 监听字段 "com.android.captiveportallogin"
],
"sniff": true,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv4"
}
]
} }
``` ```
@@ -80,19 +86,6 @@ tun 接口的 IPv6 前缀。
为避免流量环回,请设置 `route.auto_detect_interface``route.default_interface``outbound.bind_interface` 为避免流量环回,请设置 `route.auto_detect_interface``route.default_interface``outbound.bind_interface`
!!! note "与 Android VPN 一起使用"
VPN 默认优先于 tun。要使 tun 经过 VPN启用 `route.override_android_vpn`
#### strict_route
在 Linux 中启用 `auto_route` 时执行严格的路由规则。
* 让不支持的网络无法到达
* 将所有连接路由到 tun
它可以防止地址泄漏,并使 DNS 劫持在 Android 和使用 systemd-resolved 的 Linux 上工作,但你的设备将无法其他设备被访问。
#### endpoint_independent_nat #### endpoint_independent_nat
启用独立于端点的 NAT。 启用独立于端点的 NAT。
@@ -107,15 +100,14 @@ UDP NAT 过期时间,以秒为单位,默认为 3005 分钟)。
TCP/IP 栈。 TCP/IP 栈。
| 栈 | 描述 | 状态 | | 栈 | 上游 | 状态 |
|------------------|--------------------------------------------------------------------------|-------| |------------------|-----------------------------------------------------------------------|-------|
| gVisor (default) | 基于 [google/gvisor](https://github.com/google/gvisor) | 推荐 | | gVisor (default) | [google/gvisor](https://github.com/google/gvisor) | 推荐 |
| system | 兼容性较差,有时性能更好。 | 推荐 | | LWIP | [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | 上游已存档 |
| LWIP | 基于 [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | 上游已存档 |
!!! warning "" !!! warning ""
默认安装不包含 LWIP 栈,请参阅 [安装](/zh/#_2)。 默认安装不包含 LWIP 栈,请参阅 [安装](/zh/#installation)。
#### include_uid #### include_uid
@@ -160,4 +152,22 @@ TCP/IP 栈。
### 监听字段 ### 监听字段
参阅 [监听字段](/zh/configuration/shared/listen/)。 #### sniff
启用协议探测。
参阅 [协议探测](/zh/configuration/route/sniff/)。
#### sniff_override_destination
用探测出的域名覆盖连接目标地址。
如果域名无效(如 Tor将不生效。
#### domain_strategy
可选值: `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`
如果设置,请求的域名将在路由之前解析为 IP。
如果 `sniff_override_destination` 生效,它的值将作为后备。

View File

@@ -2,28 +2,34 @@
```json ```json
{ {
"type": "vmess", "inbounds": [
"tag": "vmess-in",
... // Listen Fields
"users": [
{ {
"name": "sekai", "type": "vmess",
"uuid": "bf000d23-0752-40b4-affe-68f7707a9661", "tag": "vmess-in",
"alterId": 0
"listen": "::",
"listen_port": 2080,
"tcp_fast_open": false,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"proxy_protocol": false,
"users": [
{
"name": "sekai",
"uuid": "bf000d23-0752-40b4-affe-68f7707a9661",
"alterId": 0
}
],
"tls": {},
"transport": {}
} }
], ]
"tls": {},
"transport": {}
} }
``` ```
### Listen Fields ### VMess Fields
See [Listen Fields](/configuration/shared/listen) for details.
### Fields
#### users #### users
@@ -47,3 +53,45 @@ TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
#### transport #### transport
V2Ray Transport configuration, see [V2Ray Transport](/configuration/shared/v2ray-transport). V2Ray Transport configuration, see [V2Ray Transport](/configuration/shared/v2ray-transport).
### Listen Fields
#### listen
==Required==
Listen address.
#### listen_port
==Required==
Listen port.
#### tcp_fast_open
Enable tcp fast open for listener.
#### sniff
Enable sniffing.
See [Protocol Sniff](/configuration/route/sniff/) for details.
#### sniff_override_destination
Override the connection destination address with the sniffed domain.
If the domain name is invalid (like tor), this will not work.
#### domain_strategy
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
If set, the requested domain name will be resolved to IP before routing.
If `sniff_override_destination` is in effect, its value will be taken as a fallback.
#### proxy_protocol
Parse [Proxy Protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) in the connection header.

View File

@@ -2,28 +2,34 @@
```json ```json
{ {
"type": "vmess", "inbounds": [
"tag": "vmess-in",
... // 监听字段
"users": [
{ {
"name": "sekai", "type": "vmess",
"uuid": "bf000d23-0752-40b4-affe-68f7707a9661", "tag": "vmess-in",
"alterId": 0
"listen": "::",
"listen_port": 2080,
"tcp_fast_open": false,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"proxy_protocol": false,
"users": [
{
"name": "sekai",
"uuid": "bf000d23-0752-40b4-affe-68f7707a9661",
"alterId": 0
}
],
"tls": {},
"transport": {}
} }
], ]
"tls": {},
"transport": {}
} }
``` ```
### 监听字段 ### VMess 字段
参阅 [监听字段](/zh/configuration/shared/listen/)。
### 字段
#### users #### users
@@ -47,3 +53,45 @@ TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
#### transport #### transport
V2Ray 传输配置,参阅 [V2Ray 传输层](/zh/configuration/shared/v2ray-transport)。 V2Ray 传输配置,参阅 [V2Ray 传输层](/zh/configuration/shared/v2ray-transport)。
### 监听字段
#### listen
==必填==
监听地址。
#### listen_port
==必填==
监听端口。
#### tcp_fast_open
为监听器启用 TCP 快速打开。
#### sniff
启用协议探测。
参阅 [协议探测](/zh/configuration/route/sniff/)。
#### sniff_override_destination
用探测出的域名覆盖连接目标地址。
如果域名无效(如 Tor将不生效。
#### domain_strategy
可选值: `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`
如果设置,请求的域名将在路由之前解析为 IP。
如果 `sniff_override_destination` 生效,它的值将作为后备。
#### proxy_protocol
解析连接头中的 [代理协议](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)。

View File

@@ -8,8 +8,8 @@ sing-box uses JSON for configuration files.
{ {
"log": {}, "log": {},
"dns": {}, "dns": {},
"inbounds": [], "inbounds": {},
"outbounds": [], "outbounds": {},
"route": {}, "route": {},
"experimental": {} "experimental": {}
} }

View File

@@ -8,8 +8,8 @@ sing-box 使用 JSON 作为配置文件格式。
{ {
"log": {}, "log": {},
"dns": {}, "dns": {},
"inbounds": [], "inbounds": {},
"outbounds": [], "outbounds": {},
"route": {}, "route": {},
"experimental": {} "experimental": {}
} }

View File

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

View File

@@ -1,5 +1,3 @@
# 日志
### 结构 ### 结构
```json ```json

View File

@@ -4,8 +4,12 @@
```json ```json
{ {
"type": "block", "outbounds": [
"tag": "block" {
"type": "block",
"tag": "block"
}
]
} }
``` ```

View File

@@ -4,8 +4,12 @@
```json ```json
{ {
"type": "block", "outbounds": [
"tag": "block" {
"type": "block",
"tag": "block"
}
]
} }
``` ```

View File

@@ -4,18 +4,30 @@
```json ```json
{ {
"type": "direct", "outbounds": [
"tag": "direct-out", {
"type": "direct",
"override_address": "1.0.0.1", "tag": "direct-out",
"override_port": 53,
"proxy_protocol": 0, "override_address": "1.0.0.1",
"override_port": 53,
... // Dial Fields "proxy_protocol": 0,
"detour": "upstream-out",
"bind_interface": "en0",
"bind_address": "0.0.0.0",
"routing_mark": 1234,
"reuse_addr": false,
"connect_timeout": "5s",
"tcp_fast_open": false,
"domain_strategy": "prefer_ipv6",
"fallback_delay": "300ms"
}
]
} }
``` ```
### Fields ### Direct Fields
#### override_address #### override_address
@@ -33,4 +45,54 @@ Protocol value can be `1` or `2`.
### Dial Fields ### Dial Fields
See [Dial Fields](/configuration/shared/dial) for details. #### detour
The tag of the upstream outbound.
Other dial fields will be ignored when enabled.
#### bind_interface
The network interface to bind to.
#### bind_address
The address to bind to.
#### routing_mark
!!! error ""
Only supported on Linux.
Set netfilter routing mark.
#### reuse_addr
Reuse listener address.
#### connect_timeout
Connect timeout, in golang's Duration format.
A duration string is a possibly signed sequence of
decimal numbers, each with optional fraction and a unit suffix,
such as "300ms", "-1.5h" or "2h45m".
Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
#### domain_strategy
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
If set, the requested domain name will be resolved to IP before connect.
`dns.strategy` will be used if empty.
#### fallback_delay
The length of time to wait before spawning a RFC 6555 Fast Fallback connection.
That is, is the amount of time to wait for IPv6 to succeed before assuming
that IPv6 is misconfigured and falling back to IPv4 if `prefer_ipv4` is set.
If zero, a default delay of 300ms is used.
Only take effect when `domain_strategy` is `prefer_ipv4` or `prefer_ipv6`.

View File

@@ -4,18 +4,30 @@
```json ```json
{ {
"type": "direct", "outbounds": [
"tag": "direct-out", {
"type": "direct",
"override_address": "1.0.0.1", "tag": "direct-out",
"override_port": 53,
"proxy_protocol": 0, "override_address": "1.0.0.1",
"override_port": 53,
... // 拨号字段 "proxy_protocol": 0,
"detour": "upstream-out",
"bind_interface": "en0",
"bind_address": "0.0.0.0",
"routing_mark": 1234,
"reuse_addr": false,
"connect_timeout": "5s",
"tcp_fast_open": false,
"domain_strategy": "prefer_ipv6",
"fallback_delay": "300ms"
}
]
} }
``` ```
### 字段 ### Direct 字段
#### override_address #### override_address
@@ -33,4 +45,51 @@
### 拨号字段 ### 拨号字段
参阅 [拨号字段](/zh/configuration/shared/dial/)。 #### detour
上游出站的标签。
启用时,其他拨号字段将被忽略。
#### bind_interface
要绑定到的网络接口。
#### bind_address
要绑定的地址。
#### routing_mark
!!! error ""
仅支持 Linux。
设置 netfilter 路由标记。
#### reuse_addr
重用监听地址。
#### connect_timeout
连接超时,采用 golang 的 Duration 格式。
持续时间字符串是一个可能有符号的序列十进制数,每个都有可选的分数和单位后缀, 例如 "300ms"、"-1.5h" 或 "2h45m"。
有效时间单位为 "ns"、"us"(或 "µs")、"ms"、"s"、"m"、"h"。
#### domain_strategy
可选值:`prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`
如果设置,域名将在请求发出之前解析为 IP。
默认使用 `dns.strategy`
#### fallback_delay
在生成 RFC 6555 快速回退连接之前等待的时间长度。
也就是说,是在假设之前等待 IPv6 成功的时间量如果设置了 "prefer_ipv4",则 IPv6 配置错误并回退到 IPv4。
如果为零,则使用 300 毫秒的默认延迟。
仅当 `domain_strategy``prefer_ipv4``prefer_ipv6` 时生效。

View File

@@ -4,8 +4,12 @@
```json ```json
{ {
"type": "dns", "outbounds": [
"tag": "dns-out" {
"type": "dns",
"tag": "dns-out"
}
]
} }
``` ```

View File

@@ -4,8 +4,12 @@
```json ```json
{ {
"type": "dns", "outbounds": [
"tag": "dns-out" {
"type": "dns",
"tag": "dns-out"
}
]
} }
``` ```

View File

@@ -4,20 +4,32 @@
```json ```json
{ {
"type": "http", "outbounds": [
"tag": "http-out", {
"type": "http",
"server": "127.0.0.1", "tag": "http-out",
"server_port": 1080,
"username": "sekai", "server": "127.0.0.1",
"password": "admin", "server_port": 1080,
"tls": {}, "username": "sekai",
"password": "admin",
... // Dial Fields "tls": {},
"detour": "upstream-out",
"bind_interface": "en0",
"bind_address": "0.0.0.0",
"routing_mark": 1234,
"reuse_addr": false,
"connect_timeout": "5s",
"tcp_fast_open": false,
"domain_strategy": "prefer_ipv6",
"fallback_delay": "300ms"
}
]
} }
``` ```
### Fields ### HTTP Fields
#### server #### server
@@ -45,4 +57,54 @@ TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
### Dial Fields ### Dial Fields
See [Dial Fields](/configuration/shared/dial) for details. #### detour
The tag of the upstream outbound.
Other dial fields will be ignored when enabled.
#### bind_interface
The network interface to bind to.
#### bind_address
The address to bind to.
#### routing_mark
!!! error ""
Only supported on Linux.
Set netfilter routing mark.
#### reuse_addr
Reuse listener address.
#### connect_timeout
Connect timeout, in golang's Duration format.
A duration string is a possibly signed sequence of
decimal numbers, each with optional fraction and a unit suffix,
such as "300ms", "-1.5h" or "2h45m".
Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
#### domain_strategy
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
If set, the server domain name will be resolved to IP before connecting.
`dns.strategy` will be used if empty.
#### fallback_delay
The length of time to wait before spawning a RFC 6555 Fast Fallback connection.
That is, is the amount of time to wait for IPv6 to succeed before assuming
that IPv6 is misconfigured and falling back to IPv4 if `prefer_ipv4` is set.
If zero, a default delay of 300ms is used.
Only take effect when `domain_strategy` is `prefer_ipv4` or `prefer_ipv6`.

View File

@@ -4,20 +4,32 @@
```json ```json
{ {
"type": "http", "outbounds": [
"tag": "http-out", {
"type": "http",
"server": "127.0.0.1", "tag": "http-out",
"server_port": 1080,
"username": "sekai", "server": "127.0.0.1",
"password": "admin", "server_port": 1080,
"tls": {}, "username": "sekai",
"password": "admin",
... // 拨号字段 "tls": {},
"detour": "upstream-out",
"bind_interface": "en0",
"bind_address": "0.0.0.0",
"routing_mark": 1234,
"reuse_addr": false,
"connect_timeout": "5s",
"tcp_fast_open": false,
"domain_strategy": "prefer_ipv6",
"fallback_delay": "300ms"
}
]
} }
``` ```
### 字段 ### HTTP 字段
#### server #### server
@@ -45,4 +57,51 @@ TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
### 拨号字段 ### 拨号字段
参阅 [拨号字段](/zh/configuration/shared/dial/)。 #### detour
上游出站的标签。
启用时,其他拨号字段将被忽略。
#### bind_interface
要绑定到的网络接口。
#### bind_address
要绑定的地址。
#### routing_mark
!!! error ""
仅支持 Linux。
设置 netfilter 路由标记。
#### reuse_addr
重用监听地址。
#### connect_timeout
连接超时,采用 golang 的 Duration 格式。
持续时间字符串是一个可能有符号的序列十进制数,每个都有可选的分数和单位后缀, 例如 "300ms"、"-1.5h" 或 "2h45m"。
有效时间单位为 "ns"、"us"(或 "µs")、"ms"、"s"、"m"、"h"。
#### domain_strategy
可选值:`prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`
如果设置,服务器域名将在连接前解析为 IP。
默认使用 `dns.strategy`
#### fallback_delay
在生成 RFC 6555 快速回退连接之前等待的时间长度。
也就是说,是在假设之前等待 IPv6 成功的时间量如果设置了 "prefer_ipv4",则 IPv6 配置错误并回退到 IPv4。
如果为零,则使用 300 毫秒的默认延迟。
仅当 `domain_strategy``prefer_ipv4``prefer_ipv6` 时生效。

View File

@@ -2,25 +2,37 @@
```json ```json
{ {
"type": "hysteria", "outbounds": [
"tag": "hysteria-out", {
"type": "hysteria",
"server": "127.0.0.1", "tag": "hysteria-out",
"server_port": 1080,
"up": "100 Mbps", "server": "127.0.0.1",
"up_mbps": 100, "server_port": 1080,
"down": "100 Mbps",
"down_mbps": 100, "up": "100 Mbps",
"obfs": "fuck me till the daylight", "up_mbps": 100,
"auth": "", "down": "100 Mbps",
"auth_str": "password", "down_mbps": 100,
"recv_window_conn": 0, "obfs": "fuck me till the daylight",
"recv_window": 0, "auth": "",
"disable_mtu_discovery": false, "auth_str": "password",
"network": "tcp", "recv_window_conn": 0,
"tls": {}, "recv_window": 0,
"disable_mtu_discovery": false,
... // Dial Fields "network": "tcp",
"tls": {},
"detour": "upstream-out",
"bind_interface": "en0",
"bind_address": "0.0.0.0",
"routing_mark": 1234,
"reuse_addr": false,
"connect_timeout": "5s",
"domain_strategy": "prefer_ipv6",
"fallback_delay": "300ms"
}
]
} }
``` ```
@@ -28,7 +40,7 @@
QUIC, which is required by hysteria is not included by default, see [Installation](/#installation). QUIC, which is required by hysteria is not included by default, see [Installation](/#installation).
### Fields ### Hysteria Fields
#### server #### server
@@ -113,4 +125,54 @@ Both is enabled by default.
### Dial Fields ### Dial Fields
See [Dial Fields](/configuration/shared/dial) for details. #### detour
The tag of the upstream outbound.
Other dial fields will be ignored when enabled.
#### bind_interface
The network interface to bind to.
#### bind_address
The address to bind to.
#### routing_mark
!!! error ""
Only supported on Linux.
Set netfilter routing mark.
#### reuse_addr
Reuse listener address.
#### connect_timeout
Connect timeout, in golang's Duration format.
A duration string is a possibly signed sequence of
decimal numbers, each with optional fraction and a unit suffix,
such as "300ms", "-1.5h" or "2h45m".
Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
#### domain_strategy
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
If set, the server domain name will be resolved to IP before connecting.
`dns.strategy` will be used if empty.
#### fallback_delay
The length of time to wait before spawning a RFC 6555 Fast Fallback connection.
That is, is the amount of time to wait for IPv6 to succeed before assuming
that IPv6 is misconfigured and falling back to IPv4 if `prefer_ipv4` is set.
If zero, a default delay of 300ms is used.
Only take effect when `domain_strategy` is `prefer_ipv4` or `prefer_ipv6`.

View File

@@ -2,33 +2,45 @@
```json ```json
{ {
"type": "hysteria", "outbounds": [
"tag": "hysteria-out", {
"type": "hysteria",
"server": "127.0.0.1", "tag": "hysteria-out",
"server_port": 1080,
"up": "100 Mbps", "server": "127.0.0.1",
"up_mbps": 100, "server_port": 1080,
"down": "100 Mbps",
"down_mbps": 100, "up": "100 Mbps",
"obfs": "fuck me till the daylight", "up_mbps": 100,
"auth": "", "down": "100 Mbps",
"auth_str": "password", "down_mbps": 100,
"recv_window_conn": 0, "obfs": "fuck me till the daylight",
"recv_window": 0, "auth": "",
"disable_mtu_discovery": false, "auth_str": "password",
"network": "tcp", "recv_window_conn": 0,
"tls": {}, "recv_window": 0,
"disable_mtu_discovery": false,
... // 拨号字段 "network": "tcp",
"tls": {},
"detour": "upstream-out",
"bind_interface": "en0",
"bind_address": "0.0.0.0",
"routing_mark": 1234,
"reuse_addr": false,
"connect_timeout": "5s",
"domain_strategy": "prefer_ipv6",
"fallback_delay": "300ms"
}
]
} }
``` ```
!!! warning "" !!! warning ""
默认安装不包含被 Hysteria 依赖的 QUIC参阅 [安装](/zh/#_2)。 默认安装不包含被 Hysteria 依赖的 QUIC参阅 [安装](/zh/#installation)。
### 字段 ### Hysteria 字段
#### server #### server
@@ -111,4 +123,51 @@ TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
### 拨号字段 ### 拨号字段
参阅 [拨号字段](/zh/configuration/shared/dial/)。 #### detour
上游出站的标签。
启用时,其他拨号字段将被忽略。
#### bind_interface
要绑定到的网络接口。
#### bind_address
要绑定的地址。
#### routing_mark
!!! error ""
仅支持 Linux。
设置 netfilter 路由标记。
#### reuse_addr
重用监听地址
#### connect_timeout
连接超时,采用 golang 的 Duration 格式。
持续时间字符串是一个可能有符号的序列十进制数,每个都有可选的分数和单位后缀, 例如 "300ms"、"-1.5h" 或 "2h45m"。
有效时间单位为 "ns"、"us"(或 "µs")、"ms"、"s"、"m"、"h"。
#### domain_strategy
可选值:`prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`
如果设置,服务器域名将在连接前解析为 IP。
默认使用 `dns.strategy`
#### fallback_delay
在生成 RFC 6555 快速回退连接之前等待的时间长度。
也就是说,是在假设之前等待 IPv6 成功的时间量如果设置了 "prefer_ipv4",则 IPv6 配置错误并回退到 IPv4。
如果为零,则使用 300 毫秒的默认延迟。
仅当 `domain_strategy``prefer_ipv4``prefer_ipv6` 时生效。

View File

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

View File

@@ -2,15 +2,19 @@
```json ```json
{ {
"type": "selector",
"tag": "select",
"outbounds": [ "outbounds": [
"proxy-a", {
"proxy-b", "type": "selector",
"proxy-c" "tag": "select",
],
"default": "proxy-c" "outbounds": [
"proxy-a",
"proxy-b",
"proxy-c"
],
"default": "proxy-c"
}
]
} }
``` ```

View File

@@ -2,15 +2,19 @@
```json ```json
{ {
"type": "selector",
"tag": "select",
"outbounds": [ "outbounds": [
"proxy-a", {
"proxy-b", "type": "selector",
"proxy-c" "tag": "select",
],
"default": "proxy-c" "outbounds": [
"proxy-a",
"proxy-b",
"proxy-c"
],
"default": "proxy-c"
}
]
} }
``` ```

View File

@@ -2,24 +2,34 @@
```json ```json
{ {
"type": "shadowsocks", "outbounds": [
"tag": "ss-out", {
"type": "shadowsocks",
"server": "127.0.0.1", "tag": "ss-out",
"server_port": 1080,
"method": "2022-blake3-aes-128-gcm", "server": "127.0.0.1",
"password": "8JCsPssfgS8tiRwiMlhARg==", "server_port": 1080,
"plugin": "", "method": "2022-blake3-aes-128-gcm",
"plugin_opts": "", "password": "8JCsPssfgS8tiRwiMlhARg==",
"network": "udp", "network": "udp",
"udp_over_tcp": false, "udp_over_tcp": false,
"multiplex": {}, "multiplex": {},
... // Dial Fields "detour": "upstream-out",
"bind_interface": "en0",
"bind_address": "0.0.0.0",
"routing_mark": 1234,
"reuse_addr": false,
"connect_timeout": "5s",
"tcp_fast_open": false,
"domain_strategy": "prefer_ipv6",
"fallback_delay": "300ms"
}
]
} }
``` ```
### Fields ### Shadowsocks Fields
#### server #### server
@@ -67,16 +77,6 @@ Legacy encryption methods:
The shadowsocks password. The shadowsocks password.
#### plugin
Shadowsocks SIP003 plugin, implemented in internal.
Only `obfs-local` and `v2ray-plugin` are supported.
#### plugin_opts
Shadowsocks SIP003 plugin options.
#### network #### network
Enabled network Enabled network
@@ -97,4 +97,54 @@ Multiplex configuration, see [Multiplex](/configuration/shared/multiplex).
### Dial Fields ### Dial Fields
See [Dial Fields](/configuration/shared/dial) for details. #### detour
The tag of the upstream outbound.
Other dial fields will be ignored when enabled.
#### bind_interface
The network interface to bind to.
#### bind_address
The address to bind to.
#### routing_mark
!!! error ""
Only supported on Linux.
Set netfilter routing mark.
#### reuse_addr
Reuse listener address.
#### connect_timeout
Connect timeout, in golang's Duration format.
A duration string is a possibly signed sequence of
decimal numbers, each with optional fraction and a unit suffix,
such as "300ms", "-1.5h" or "2h45m".
Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
#### domain_strategy
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
If set, the server domain name will be resolved to IP before connecting.
`dns.strategy` will be used if empty.
#### fallback_delay
The length of time to wait before spawning a RFC 6555 Fast Fallback connection.
That is, is the amount of time to wait for IPv6 to succeed before assuming
that IPv6 is misconfigured and falling back to IPv4 if `prefer_ipv4` is set.
If zero, a default delay of 300ms is used.
Only take effect when `domain_strategy` is `prefer_ipv4` or `prefer_ipv6`.

View File

@@ -1,25 +1,35 @@
### 结构 ### Structure
```json ```json
{ {
"type": "shadowsocks", "outbounds": [
"tag": "ss-out", {
"type": "shadowsocks",
"server": "127.0.0.1", "tag": "ss-out",
"server_port": 1080,
"method": "2022-blake3-aes-128-gcm", "server": "127.0.0.1",
"password": "8JCsPssfgS8tiRwiMlhARg==", "server_port": 1080,
"plugin": "", "method": "2022-blake3-aes-128-gcm",
"plugin_opts": "", "password": "8JCsPssfgS8tiRwiMlhARg==",
"network": "udp", "network": "udp",
"udp_over_tcp": false, "udp_over_tcp": false,
"multiplex": {}, "multiplex": {},
... // 拨号字段 "detour": "upstream-out",
"bind_interface": "en0",
"bind_address": "0.0.0.0",
"routing_mark": 1234,
"reuse_addr": false,
"connect_timeout": "5s",
"tcp_fast_open": false,
"domain_strategy": "prefer_ipv6",
"fallback_delay": "300ms"
}
]
} }
``` ```
### 字段 ### Shadowsocks 字段
#### server #### server
@@ -67,16 +77,6 @@
Shadowsocks 密码。 Shadowsocks 密码。
#### plugin
Shadowsocks SIP003 插件,由内部实现。
仅支持 `obfs-local``v2ray-plugin`
#### plugin_opts
Shadowsocks SIP003 插件参数。
#### network #### network
启用的网络协议 启用的网络协议
@@ -97,4 +97,51 @@ Shadowsocks SIP003 插件参数。
### 拨号字段 ### 拨号字段
参阅 [拨号字段](/zh/configuration/shared/dial/)。 #### detour
上游出站的标签。
启用时,其他拨号字段将被忽略。
#### bind_interface
要绑定到的网络接口。
#### bind_address
要绑定的地址。
#### routing_mark
!!! error ""
仅支持 Linux。
设置 netfilter 路由标记。
#### reuse_addr
重用监听地址。
#### connect_timeout
连接超时,采用 golang 的 Duration 格式。
持续时间字符串是一个可能有符号的序列十进制数,每个都有可选的分数和单位后缀, 例如 "300ms"、"-1.5h" 或 "2h45m"。
有效时间单位为 "ns"、"us"(或 "µs")、"ms"、"s"、"m"、"h"。
#### domain_strategy
可选值:`prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`
如果设置,服务器域名将在连接前解析为 IP。
默认使用 `dns.strategy`
#### fallback_delay
在生成 RFC 6555 快速回退连接之前等待的时间长度。
也就是说,是在假设之前等待 IPv6 成功的时间量如果设置了 "prefer_ipv4",则 IPv6 配置错误并回退到 IPv4。
如果为零,则使用 300 毫秒的默认延迟。
仅当 `domain_strategy``prefer_ipv4``prefer_ipv6` 时生效。

View File

@@ -1,106 +0,0 @@
### Structure
```json
{
"type": "shadowsocksr",
"tag": "ssr-out",
"server": "127.0.0.1",
"server_port": 1080,
"method": "aes-128-cfb",
"password": "8JCsPssfgS8tiRwiMlhARg==",
"obfs": "plain",
"obfs_param": "",
"protocol": "origin",
"protocol_param": "",
"network": "udp",
... // Dial Fields
}
```
!!! warning ""
The ShadowsocksR protocol is obsolete and unmaintained. This outbound is provided for compatibility only.
!!! warning ""
ShadowsocksR is not included by default, see [Installation](/#installation).
### Fields
#### server
==Required==
The server address.
#### server_port
==Required==
The server port.
#### method
==Required==
Encryption methods:
* `aes-128-ctr`
* `aes-192-ctr`
* `aes-256-ctr`
* `aes-128-cfb`
* `aes-192-cfb`
* `aes-256-cfb`
* `rc4-md5`
* `chacha20-ietf`
* `xchacha20`
#### password
==Required==
The shadowsocks password.
#### obfs
The ShadowsocksR obfuscate.
* plain
* http_simple
* http_post
* random_head
* tls1.2_ticket_auth
#### obfs_param
The ShadowsocksR obfuscate parameter.
#### protocol
The ShadowsocksR protocol.
* origin
* verify_sha1
* auth_sha1_v4
* auth_aes128_md5
* auth_aes128_sha1
* auth_chain_a
* auth_chain_b
#### protocol_param
The ShadowsocksR protocol parameter.
#### network
Enabled network
One of `tcp` `udp`.
Both is enabled by default.
### Dial Fields
See [Dial Fields](/configuration/shared/dial) for details.

View File

@@ -1,106 +0,0 @@
### 结构
```json
{
"type": "shadowsocksr",
"tag": "ssr-out",
"server": "127.0.0.1",
"server_port": 1080,
"method": "aes-128-cfb",
"password": "8JCsPssfgS8tiRwiMlhARg==",
"obfs": "plain",
"obfs_param": "",
"protocol": "origin",
"protocol_param": "",
"network": "udp",
... // 拨号字段
}
```
!!! warning ""
ShadowsocksR 协议已过时且无人维护。 提供此出站仅出于兼容性目的。
!!! warning ""
默认安装不包含被 ShadowsocksR参阅 [安装](/zh/#_2)。
### 字段
#### server
==必填==
服务器地址。
#### server_port
==必填==
服务器端口。
#### method
==必填==
加密方法:
* `aes-128-ctr`
* `aes-192-ctr`
* `aes-256-ctr`
* `aes-128-cfb`
* `aes-192-cfb`
* `aes-256-cfb`
* `rc4-md5`
* `chacha20-ietf`
* `xchacha20`
#### password
==必填==
Shadowsocks 密码。
#### obfs
ShadowsocksR 混淆。
* plain
* http_simple
* http_post
* random_head
* tls1.2_ticket_auth
#### obfs_param
ShadowsocksR 混淆参数。
#### protocol
ShadowsocksR 协议。
* origin
* verify_sha1
* auth_sha1_v4
* auth_aes128_md5
* auth_aes128_sha1
* auth_chain_a
* auth_chain_b
#### protocol_param
ShadowsocksR 协议参数。
#### network
启用的网络协议
`tcp``udp`
默认所有。
### 拨号字段
参阅 [拨号字段](/zh/configuration/shared/dial/)。

View File

@@ -1,38 +0,0 @@
### Structure
```json
{
"type": "shadowtls",
"tag": "st-out",
"server": "127.0.0.1",
"server_port": 1080,
"tls": {},
... // Dial Fields
}
```
### Fields
#### server
==Required==
The server address.
#### server_port
==Required==
The server port.
#### tls
==Required==
TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
### Dial Fields
See [Dial Fields](/configuration/shared/dial) for details.

View File

@@ -1,38 +0,0 @@
### 结构
```json
{
"type": "shadowtls",
"tag": "st-out",
"server": "127.0.0.1",
"server_port": 1080,
"tls": {},
... // 拨号字段
}
```
### 字段
#### server
==必填==
服务器地址。
#### server_port
==必填==
服务器端口。
#### tls
==必填==
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
### 拨号字段
参阅 [拨号字段](/zh/configuration/shared/dial/)。

View File

@@ -4,22 +4,34 @@
```json ```json
{ {
"type": "socks", "outbounds": [
"tag": "socks-out", {
"type": "socks",
"server": "127.0.0.1", "tag": "socks-out",
"server_port": 1080,
"version": "5", "server": "127.0.0.1",
"username": "sekai", "server_port": 1080,
"password": "admin", "version": "5",
"network": "udp", "username": "sekai",
"udp_over_tcp": false, "password": "admin",
"network": "udp",
"udp_over_tcp": false,
... // Dial Fields "detour": "upstream-out",
"bind_interface": "en0",
"bind_address": "0.0.0.0",
"routing_mark": 1234,
"reuse_addr": false,
"connect_timeout": "5s",
"tcp_fast_open": false,
"domain_strategy": "prefer_ipv6",
"fallback_delay": "300ms"
}
]
} }
``` ```
### Fields ### SOCKS Fields
#### server #### server
@@ -61,4 +73,54 @@ Enable the UDP over TCP protocol.
### Dial Fields ### Dial Fields
See [Dial Fields](/configuration/shared/dial) for details. #### detour
The tag of the upstream outbound.
Other dial fields will be ignored when enabled.
#### bind_interface
The network interface to bind to.
#### bind_address
The address to bind to.
#### routing_mark
!!! error ""
Only supported on Linux.
Set netfilter routing mark.
#### reuse_addr
Reuse listener address.
#### connect_timeout
Connect timeout, in golang's Duration format.
A duration string is a possibly signed sequence of
decimal numbers, each with optional fraction and a unit suffix,
such as "300ms", "-1.5h" or "2h45m".
Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
#### domain_strategy
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
If set, the server domain name will be resolved to IP before connecting.
`dns.strategy` will be used if empty.
#### fallback_delay
The length of time to wait before spawning a RFC 6555 Fast Fallback connection.
That is, is the amount of time to wait for IPv6 to succeed before assuming
that IPv6 is misconfigured and falling back to IPv4 if `prefer_ipv4` is set.
If zero, a default delay of 300ms is used.
Only take effect when `domain_strategy` is `prefer_ipv4` or `prefer_ipv6`.

Some files were not shown because too many files have changed in this diff Show More