Compare commits

..

32 Commits

Author SHA1 Message Date
世界
c6ef276811 Update dependencies 2022-08-28 12:21:22 +08:00
世界
1701aaf78c Add docker image 2022-08-28 00:23:41 +08:00
世界
122daa4bfb Simplify server installation 2022-08-28 00:23:41 +08:00
世界
561a9e5275 Update documentation 2022-08-28 00:23:41 +08:00
Hellojack
de2453fce9 Add gun-lite gRPC implementation (#44) 2022-08-27 21:05:15 +08:00
世界
d59d40c118 Fix sniff override destination 2022-08-27 14:37:14 +08:00
rand0mgh0st
3469df001f Fix documentation for socks inbound (#42) 2022-08-27 13:16:04 +08:00
世界
0d8cfa3031 Add vmess packetaddr option 2022-08-27 11:28:01 +08:00
世界
0289586880 Add documentation for strict_route 2022-08-27 09:31:17 +08:00
rand0mgh0st
e46427c7fc docs-zh-CN: use English for License section (#40) 2022-08-26 23:21:32 +08:00
世界
3ea59d9a8e Move documentation branch to main 2022-08-26 21:53:46 +08:00
世界
e85dfc6adf Add strict_route option 2022-08-26 21:53:08 +08:00
世界
d0703b78fa Fix dns hijack on android
iproute2 on android does not support port rules
2022-08-26 21:05:45 +08:00
世界
432e6adf3e Fix TLS documentation 2022-08-26 18:36:56 +08:00
世界
a057754035 Revert linux process searcher 2022-08-26 17:36:06 +08:00
世界
0348ace253 Initial release 2022-08-26 16:40:37 +08:00
世界
c5e38203eb Fix read DNS message 2022-08-26 13:35:27 +08:00
世界
9ac31d0233 Fix ipv6 route on linux 2022-08-26 12:30:31 +08:00
世界
9d8d1cd69d Update documentation 2022-08-26 11:10:02 +08:00
世界
07a0381f8b Cleanup vmessws 2022-08-26 10:22:29 +08:00
世界
f841459004 Cleanup vmesshttp 2022-08-26 08:41:45 +08:00
世界
78a26fc139 Update documentation 2022-08-25 22:49:23 +08:00
世界
9f6628445e Improve ip_cidr rule 2022-08-25 22:23:26 +08:00
世界
fa017b5977 Add contributing documentation 2022-08-25 21:08:29 +08:00
世界
58f4a970f2 Fix route connections 2022-08-25 20:48:59 +08:00
世界
021aa8faed Fix ipv6 route on linux 2022-08-25 18:57:36 +08:00
世界
83f6e037d6 Fix http proxy with compressed response 2022-08-25 18:40:13 +08:00
世界
baf153434d Fix issue template 2022-08-25 18:40:13 +08:00
世界
d481bd7993 Fix bind_address 2022-08-25 14:50:10 +08:00
Steven Tang
e859c0a6ef Fix typo in features.md (#32) 2022-08-25 13:42:22 +08:00
zakuwaki
59a39e66b1 Add trojan fallback for ALPN #31 2022-08-25 13:37:32 +08:00
世界
fd5ac69a35 Let vmess use zero instead of auto if TLS enabled 2022-08-25 11:51:17 +08:00
92 changed files with 1327 additions and 1053 deletions

View File

@@ -1,6 +1,5 @@
name: Bug Report
description: "Create a report to help us improve."
labels: [ bug ]
body:
- type: checkboxes
id: terms
@@ -56,7 +55,7 @@ body:
required: true
- type: textarea
id: config
id: log
attributes:
label: Server and client log file
value: |-

43
.github/workflows/docker.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
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

@@ -2,7 +2,7 @@ name: Generate Documents
on:
push:
branches:
- dev
- main
paths:
- docs/**
- .github/workflows/mkdocs.yml

View File

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

23
Dockerfile Normal file
View File

@@ -0,0 +1,23 @@
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 HEAD) \
&& go build -v -trimpath -tags 'with_quic,with_acme,with_wireguard,with_clash_api' \
-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
COMMIT = $(shell git rev-parse --short HEAD)
TAGS ?= with_quic,with_wireguard,with_clash_api,with_daemon
TAGS ?= with_quic,with_wireguard,with_clash_api
PARAMS = -v -trimpath -tags '$(TAGS)' -ldflags \
'-X "github.com/sagernet/sing-box/constant.Commit=$(COMMIT)" \
-w -s -buildid='
@@ -49,7 +49,14 @@ snapshot:
ghr --delete --draft --prerelease -p 1 nightly dist/release
rm -r dist
snapshot_install:
release:
goreleaser release --rm-dist --skip-publish
mkdir dist/release
mv dist/*.tar.gz dist/*.zip dist/*.deb dist/*.rpm dist/release
ghr --delete --draft --prerelease -p 3 $(shell git describe --tags) dist/release
rm -r dist
release_install:
go install -v github.com/goreleaser/goreleaser@latest
go install -v github.com/tcnksm/ghr@latest

View File

@@ -1,272 +0,0 @@
//go:build with_daemon
package main
import (
"bytes"
"io"
"net"
"net/http"
"net/url"
"os"
"github.com/sagernet/sing-box/common/json"
"github.com/sagernet/sing-box/experimental/daemon"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
"github.com/spf13/cobra"
)
var commandDaemon = &cobra.Command{
Use: "daemon",
}
func init() {
commandDaemon.AddCommand(commandDaemonInstall)
commandDaemon.AddCommand(commandDaemonUninstall)
commandDaemon.AddCommand(commandDaemonStart)
commandDaemon.AddCommand(commandDaemonStop)
commandDaemon.AddCommand(commandDaemonRestart)
commandDaemon.AddCommand(commandDaemonRun)
mainCommand.AddCommand(commandDaemon)
mainCommand.AddCommand(commandStart)
mainCommand.AddCommand(commandStop)
mainCommand.AddCommand(commandStatus)
}
var commandDaemonInstall = &cobra.Command{
Use: "install",
Short: "Install daemon",
Run: func(cmd *cobra.Command, args []string) {
err := installDaemon()
if err != nil {
log.Fatal(err)
}
},
Args: cobra.NoArgs,
}
var commandDaemonUninstall = &cobra.Command{
Use: "uninstall",
Short: "Uninstall daemon",
Run: func(cmd *cobra.Command, args []string) {
err := uninstallDaemon()
if err != nil {
log.Fatal(err)
}
},
Args: cobra.NoArgs,
}
var commandDaemonStart = &cobra.Command{
Use: "start",
Short: "Start daemon",
Run: func(cmd *cobra.Command, args []string) {
err := startDaemon()
if err != nil {
log.Fatal(err)
}
},
Args: cobra.NoArgs,
}
var commandDaemonStop = &cobra.Command{
Use: "stop",
Short: "Stop daemon",
Run: func(cmd *cobra.Command, args []string) {
err := stopDaemon()
if err != nil {
log.Fatal(err)
}
},
Args: cobra.NoArgs,
}
var commandDaemonRestart = &cobra.Command{
Use: "restart",
Short: "Restart daemon",
Run: func(cmd *cobra.Command, args []string) {
err := restartDaemon()
if err != nil {
log.Fatal(err)
}
},
Args: cobra.NoArgs,
}
var commandDaemonRun = &cobra.Command{
Use: "run",
Short: "Run daemon",
Run: func(cmd *cobra.Command, args []string) {
err := runDaemon()
if err != nil {
log.Fatal(err)
}
},
Args: cobra.NoArgs,
}
func installDaemon() error {
instance, err := daemon.New()
if err != nil {
return err
}
return instance.Install()
}
func uninstallDaemon() error {
instance, err := daemon.New()
if err != nil {
return err
}
return instance.Uninstall()
}
func startDaemon() error {
instance, err := daemon.New()
if err != nil {
return err
}
return instance.Start()
}
func stopDaemon() error {
instance, err := daemon.New()
if err != nil {
return err
}
return instance.Stop()
}
func restartDaemon() error {
instance, err := daemon.New()
if err != nil {
return err
}
return instance.Restart()
}
func runDaemon() error {
instance, err := daemon.New()
if err != nil {
return err
}
return instance.Run()
}
var commandStart = &cobra.Command{
Use: "start",
Short: "Start service",
Run: func(cmd *cobra.Command, args []string) {
err := startService()
if err != nil {
log.Fatal(err)
}
},
Args: cobra.NoArgs,
}
var commandStop = &cobra.Command{
Use: "stop",
Short: "Stop service",
Run: func(cmd *cobra.Command, args []string) {
err := stopService()
if err != nil {
log.Fatal(err)
}
},
Args: cobra.NoArgs,
}
var commandStatus = &cobra.Command{
Use: "status",
Short: "Check service",
Run: func(cmd *cobra.Command, args []string) {
err := checkService()
if err != nil {
log.Fatal(err)
}
},
Args: cobra.NoArgs,
}
func doRequest(method string, path string, params url.Values, body io.ReadCloser) ([]byte, error) {
requestURL := url.URL{
Scheme: "http",
Path: path,
Host: net.JoinHostPort("127.0.0.1", F.ToString(daemon.DefaultDaemonPort)),
}
if params != nil {
requestURL.RawQuery = params.Encode()
}
request, err := http.NewRequest(method, requestURL.String(), body)
if err != nil {
return nil, err
}
response, err := http.DefaultClient.Do(request)
if err != nil {
return nil, err
}
defer response.Body.Close()
var content []byte
if response.StatusCode != http.StatusNoContent {
content, err = io.ReadAll(response.Body)
if err != nil {
return nil, err
}
}
if response.StatusCode != http.StatusOK && response.StatusCode != http.StatusNoContent {
return nil, E.New(string(content))
}
return content, nil
}
func ping() error {
response, err := doRequest("GET", "/ping", nil, nil)
if err != nil || string(response) != "pong" {
return E.New("daemon not running")
}
return nil
}
func startService() error {
if err := ping(); err != nil {
return err
}
configContent, err := os.ReadFile(configPath)
if err != nil {
return E.Cause(err, "read config")
}
return common.Error(doRequest("POST", "/run", nil, io.NopCloser(bytes.NewReader(configContent))))
}
func stopService() error {
if err := ping(); err != nil {
return err
}
return common.Error(doRequest("GET", "/stop", nil, nil))
}
func checkService() error {
if err := ping(); err != nil {
return err
}
response, err := doRequest("GET", "/status", nil, nil)
if err != nil {
return err
}
var statusResponse daemon.StatusResponse
err = json.Unmarshal(response, &statusResponse)
if err != nil {
return err
}
if statusResponse.Running {
log.Info("service running")
} else {
log.Info("service stopped")
}
return nil
}

View File

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

View File

@@ -1,19 +0,0 @@
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

@@ -64,25 +64,25 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
var listener net.ListenConfig
if options.BindInterface != "" {
warnBindInterfaceOnUnsupportedPlatform.Check()
bindFunc := skipIfPrivate(control.BindToInterface(router.InterfaceBindManager(), options.BindInterface))
bindFunc := control.BindToInterface(router.InterfaceBindManager(), options.BindInterface)
dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
} else if router.AutoDetectInterface() {
if C.IsWindows {
bindFunc := skipIfPrivate(control.BindToInterfaceIndexFunc(func() int {
return router.InterfaceMonitor().DefaultInterfaceIndex()
}))
bindFunc := control.BindToInterfaceIndexFunc(func(network, address string) int {
return router.InterfaceMonitor().DefaultInterfaceIndex(M.ParseSocksaddr(address).Addr)
})
dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
} else {
bindFunc := skipIfPrivate(control.BindToInterfaceFunc(router.InterfaceBindManager(), func() string {
return router.InterfaceMonitor().DefaultInterfaceName()
}))
bindFunc := control.BindToInterfaceFunc(router.InterfaceBindManager(), func(network, address string) string {
return router.InterfaceMonitor().DefaultInterfaceName(M.ParseSocksaddr(address).Addr)
})
dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
}
} else if router.DefaultInterface() != "" {
bindFunc := skipIfPrivate(control.BindToInterface(router.InterfaceBindManager(), router.DefaultInterface()))
bindFunc := control.BindToInterface(router.InterfaceBindManager(), router.DefaultInterface())
dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
}
@@ -113,7 +113,10 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
}
var bindUDPAddr string
udpDialer := dialer
bindAddress := netip.Addr(options.BindAddress)
var bindAddress netip.Addr
if options.BindAddress != nil {
bindAddress = options.BindAddress.Build()
}
if bindAddress.IsValid() {
dialer.LocalAddr = &net.TCPAddr{
IP: bindAddress.AsSlice(),

View File

@@ -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) {
socket, err := resolveSocketByNetlink(network, source, destination)
_, uid, err := resolveSocketByNetlink(network, source, destination)
if err != nil {
return nil, err
}
if sharedPackage, loaded := s.packageManager.SharedPackageByID(socket.UID); loaded {
if sharedPackage, loaded := s.packageManager.SharedPackageByID(uid); loaded {
return &Info{
UserId: int32(socket.UID),
UserId: int32(uid),
PackageName: sharedPackage,
}, nil
}
if packageName, loaded := s.packageManager.PackageByID(socket.UID); loaded {
if packageName, loaded := s.packageManager.PackageByID(uid); loaded {
return &Info{
UserId: int32(socket.UID),
UserId: int32(uid),
PackageName: packageName,
}, nil
}
return &Info{UserId: int32(socket.UID)}, nil
return &Info{UserId: int32(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) {
socket, err := resolveSocketByNetlink(network, source, destination)
inode, uid, err := resolveSocketByNetlink(network, source, destination)
if err != nil {
return nil, err
}
processPath, err := resolveProcessNameByProcSearch(socket.INode, socket.UID)
processPath, err := resolveProcessNameByProcSearch(inode, uid)
if err != nil {
s.logger.DebugContext(ctx, "find process path: ", err)
}
return &Info{
UserId: int32(socket.UID),
UserId: int32(uid),
ProcessPath: processPath,
}, nil
}

View File

@@ -6,6 +6,7 @@ import (
"bytes"
"encoding/binary"
"fmt"
"net"
"net/netip"
"os"
"path"
@@ -14,7 +15,9 @@ import (
"unicode"
"unsafe"
"github.com/sagernet/netlink"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
N "github.com/sagernet/sing/common/network"
)
@@ -34,7 +37,7 @@ const (
pathProc = "/proc"
)
func resolveSocketByNetlink(network string, source netip.AddrPort, destination netip.AddrPort) (*netlink.Socket, error) {
func resolveSocketByNetlink(network string, source netip.AddrPort, destination netip.AddrPort) (inode, uid uint32, err error) {
var family uint8
var protocol uint8
@@ -44,28 +47,110 @@ func resolveSocketByNetlink(network string, source netip.AddrPort, destination n
case N.NetworkUDP:
protocol = syscall.IPPROTO_UDP
default:
return nil, os.ErrInvalid
return 0, 0, os.ErrInvalid
}
if source.Addr().Is4() {
family = syscall.AF_INET
} else {
family = syscall.AF_INET6
}
sockets, err := netlink.SocketGet(family, protocol, source, netip.AddrPortFrom(netip.IPv6Unspecified(), 0))
if err == nil {
sockets, err = netlink.SocketGet(family, protocol, source, destination)
}
req := packSocketDiagRequest(family, protocol, source)
socket, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_DGRAM, syscall.NETLINK_INET_DIAG)
if err != nil {
return nil, err
return 0, 0, E.Cause(err, "dial netlink")
}
if len(sockets) > 1 {
for _, socket := range sockets {
if socket.ID.DestinationPort == destination.Port() {
return socket, nil
}
}
defer syscall.Close(socket)
syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, &syscall.Timeval{Usec: 100})
syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &syscall.Timeval{Usec: 100})
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) {

View File

@@ -1,6 +1,7 @@
package settings
import (
"net/netip"
"strings"
"github.com/sagernet/sing-box/adapter"
@@ -20,7 +21,7 @@ type systemProxy struct {
}
func (p *systemProxy) update() error {
newInterfaceName := p.monitor.DefaultInterfaceName()
newInterfaceName := p.monitor.DefaultInterfaceName(netip.IPv4Unspecified())
if p.interfaceName == newInterfaceName {
return nil
}

View File

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

View File

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

View File

@@ -1,4 +1,30 @@
#### 2022/08/24
#### 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 unix search path
@@ -7,7 +33,7 @@
* Fix early close on windows and catch any
* Initial zh-CN document translation
#### 2022/08/23
##### 2022/08/23
* Add [V2Ray Transport](/configuration/shared/v2ray-transport) support for VMess and Trojan
* Allow plain http request in Naive inbound (It can now be used with nginx)
@@ -16,17 +42,17 @@
* Parse X-Forward-For in HTTP requests
* Handle SIGHUP signal
#### 2022/08/22
##### 2022/08/22
* Add strategy setting for each [DNS server](/configuration/dns/server)
* Add bind address to outbound options
#### 2022/08/21
##### 2022/08/21
* Add [Tor outbound](/configuration/outbound/tor)
* Add [SSH outbound](/configuration/outbound/ssh)
#### 2022/08/20
##### 2022/08/20
* Attempt to unwrap ip-in-fqdn socksaddr
* Fix read packages in android 12
@@ -36,52 +62,52 @@
* Skip bind connection with private destination to interface
* Add [Trojan connection fallback](/configuration/inbound/trojan#fallback)
#### 2022/08/19
##### 2022/08/19
* Add Hysteria [Inbound](/configuration/inbound/hysteria) and [Outbund](/configuration/outbound/hysteria)
* Add [ACME TLS certificate issuer](/configuration/shared/tls)
* Allow read config from stdin (-c stdin)
* Update gVisor to 20220815.0
#### 2022/08/18
##### 2022/08/18
* Fix find process with lwip stack
* Fix crash on shadowsocks server
* Fix crash on darwin tun
* Fix write log to file
#### 2022/08/17
##### 2022/08/17
* Improve async dns transports
#### 2022/08/16
##### 2022/08/16
* Add ip_version (route/dns) rule item
* Add [WireGuard](/configuration/outbound/wireguard) outbound
#### 2022/08/15
##### 2022/08/15
* Add uid, android user and package rules support in [Tun](/configuration/inbound/tun) routing.
#### 2022/08/13
##### 2022/08/13
* Fix dns concurrent write
#### 2022/08/12
##### 2022/08/12
* Performance improvements
* Add UoT option for [SOCKS](/configuration/outbound/socks) outbound
#### 2022/08/11
##### 2022/08/11
* Add UoT option for [Shadowsocks](/configuration/outbound/shadowsocks) outbound, UoT support for all inbounds
#### 2022/08/10
##### 2022/08/10
* Add full-featured [Naive](/configuration/inbound/naive) inbound
* Fix default dns server option [#9] by iKirby
#### 2022/08/09
##### 2022/08/09
No changelog before.

View File

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

View File

@@ -38,7 +38,8 @@
"private"
],
"source_ip_cidr": [
"10.0.0.0/24"
"10.0.0.0/24",
"192.168.0.1"
],
"source_port": [
12345

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -36,44 +36,44 @@ SOCKS users.
No authentication required if empty.
### 监听字段
### Listen Fields
#### listen
==必填==
==Required==
监听地址
Listen address.
#### listen_port
==必填==
==Required==
监听端口
Listen port.
#### tcp_fast_open
为监听器启用 TCP 快速打开
Enable tcp fast open for listener.
#### sniff
启用协议探测。
Enable sniffing.
参阅 [协议探测](/zh/configuration/route/sniff/)
See [Protocol Sniff](/configuration/route/sniff/) for details.
#### sniff_override_destination
用探测出的域名覆盖连接目标地址。
Override the connection destination address with the sniffed domain.
如果域名无效(如 Tor将不生效。
If the domain name is invalid (like tor), this will not work.
#### domain_strategy
可选值: `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
如果设置,请求的域名将在路由之前解析为 IP。
If set, the requested domain name will be resolved to IP before routing.
如果 `sniff_override_destination` 生效,它的值将作为后备。
If `sniff_override_destination` is in effect, its value will be taken as a fallback.
#### proxy_protocol
解析连接头中的 [代理协议](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)
Parse [Proxy Protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) in the connection header.

View File

@@ -23,9 +23,15 @@
],
"tls": {},
"fallback": {
"server": "127.0.0.0.1",
"server": "127.0.0.1",
"server_port": 8080
},
"fallback_for_alpn": {
"http/1.1": {
"server": "127.0.0.1",
"server_port": 8081
}
},
"transport": {}
}
]
@@ -50,7 +56,13 @@ TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
There is no evidence that GFW detects and blocks Trojan servers based on HTTP responses, and opening the standard http/s port on the server is a much bigger signature.
Fallback server configuration. Disabled if empty.
Fallback server configuration. Disabled if `fallback` and `fallback_for_alpn` are empty.
#### fallback_for_alpn
Fallback server configuration for specified ALPN.
If not empty, TLS fallback requests with ALPN not in this table will be rejected.
#### transport

View File

@@ -6,7 +6,6 @@
{
"type": "trojan",
"tag": "trojan-in",
"listen": "::",
"listen_port": 2080,
"tcp_fast_open": false,
@@ -14,7 +13,6 @@
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"proxy_protocol": false,
"users": [
{
"name": "sekai",
@@ -23,9 +21,15 @@
],
"tls": {},
"fallback": {
"server": "127.0.0.0.1",
"server": "127.0.0.1",
"server_port": 8080
},
"fallback_for_alpn": {
"http/1.1": {
"server": "127.0.0.1",
"server_port": 8081
}
},
"transport": {}
}
]
@@ -52,7 +56,13 @@ TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
没有证据表明 GFW 基于 HTTP 响应检测并阻止木马服务器,并且在服务器上打开标准 http/s 端口是一个更大的特征。
备用服务器配置。默认禁用
回退服务器配置。如果 `fallback``fallback_for_alpn` 为空,则禁用回退
#### fallback_for_alpn
为 ALPN 指定回退服务器配置。
如果不为空ALPN 不在此列表中的 TLS 回退请求将被拒绝。
#### transport

View File

@@ -15,6 +15,7 @@
"inet6_address": "fdfe:dcba:9876::1/128",
"mtu": 1500,
"auto_route": true,
"strict_route": true,
"endpoint_independent_nat": false,
"udp_timeout": 300,
"stack": "gvisor",
@@ -86,6 +87,16 @@ Set the default route to the Tun.
To avoid traffic loopback, set `route.auto_detect_interface` or `route.default_interface` or `outbound.bind_interface`
#### 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
Enable endpoint-independent NAT.

View File

@@ -15,6 +15,7 @@
"inet6_address": "fdfe:dcba:9876::1/128",
"mtu": 1500,
"auto_route": true,
"strict_route": true,
"endpoint_independent_nat": false,
"udp_timeout": 300,
"stack": "gvisor",
@@ -86,6 +87,15 @@ tun 接口的 IPv6 前缀。
为避免流量环回,请设置 `route.auto_detect_interface``route.default_interface``outbound.bind_interface`
#### strict_route
在 Linux 中启用 `auto_route` 时执行严格的路由规则。
* 让不支持的网络无法到达
* 将所有连接路由到 tun
它可以防止地址泄漏,并使 DNS 劫持在 Android 和使用 systemd-resolved 的 Linux 上工作,但你的设备将无法其他设备被访问。
#### endpoint_independent_nat
启用独立于端点的 NAT。
@@ -107,7 +117,7 @@ TCP/IP 栈。
!!! warning ""
默认安装不包含 LWIP 栈,请参阅 [安装](/zh/#installation)。
默认安装不包含 LWIP 栈,请参阅 [安装](/zh/#_2)。
#### include_uid

View File

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

View File

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

View File

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

View File

@@ -16,6 +16,7 @@
"authenticated_length": true,
"network": "tcp",
"tls": {},
"packet_addr": false,
"multiplex": {},
"transport": {},
@@ -95,6 +96,10 @@ Both is enabled by default.
TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
#### packet_addr
Enable packetaddr support.
#### multiplex
Multiplex configuration, see [Multiplex](/configuration/shared/multiplex).

View File

@@ -6,6 +6,7 @@
{
"type": "vmess",
"tag": "vmess-out",
"server": "127.0.0.1",
"server_port": 1080,
"uuid": "bf000d23-0752-40b4-affe-68f7707a9661",
@@ -15,8 +16,10 @@
"authenticated_length": true,
"network": "tcp",
"tls": {},
"packet_addr": false,
"multiplex": {},
"transport": {},
"detour": "upstream-out",
"bind_interface": "en0",
"bind_address": "0.0.0.0",
@@ -93,6 +96,10 @@ VMess 用户 ID。
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
#### packet_addr
启用 packetaddr 支持。
#### multiplex
多路复用配置, 参阅 [多路复用](/zh/configuration/shared/multiplex)。

View File

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

View File

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

View File

@@ -41,10 +41,12 @@
"cn"
],
"source_ip_cidr": [
"10.0.0.0/24"
"10.0.0.0/24",
"192.168.0.1"
],
"ip_cidr": [
"10.0.0.0/24"
"10.0.0.0/24",
"192.168.0.1"
],
"source_port": [
12345

View File

@@ -39,6 +39,7 @@
```json
{
"enabled": true,
"disable_sni": false,
"server_name": "",
"insecure": false,
"alpn": [],
@@ -87,14 +88,18 @@ Cipher suite values:
Enable TLS.
#### disable_sni
==Client only==
Do not send server name in ClientHello.
#### server_name
Used to verify the hostname on the returned certificates unless insecure is given.
It is also included in the client's handshake to support virtual hosting unless it is an IP address.
See [Server Name Indication](https://en.wikipedia.org/wiki/Server_Name_Indication).
#### insecure
==Client only==

View File

@@ -32,13 +32,14 @@
!!! warning ""
默认安装不包含 ACME参阅 [安装](/zh/#installation)。
默认安装不包含 ACME参阅 [安装](/zh/#_2)。
### 出站
```json
{
"enabled": true,
"disable_sni": false,
"server_name": "",
"insecure": false,
"alpn": [],
@@ -87,14 +88,18 @@ TLS 版本值:
启用 TLS
#### disable_sni
==仅客户端==
不要在 ClientHello 中发送服务器名称.
#### server_name
用于验证返回证书上的主机名,除非设置不安全。
它还包含在 ClientHello 中以支持虚拟主机,除非它是 IP 地址。
参阅 [Server Name Indication](https://en.wikipedia.org/wiki/Server_Name_Indication)。
#### insecure
==仅客户端==
@@ -208,7 +213,8 @@ EAB外部帐户绑定包含将 ACME 帐户绑定或映射到其他已知
外部帐户绑定“用于将 ACME 帐户与非 ACME 系统中的现有帐户相关联,例如 CA 客户数据库。
为了启用 ACME 帐户绑定,运行 ACME 服务器的 CA 需要向 ACME 客户端提供 MAC 密钥和密钥标识符,使用 ACME 之外的一些机制。 §7.3.4
为了启用 ACME 帐户绑定,运行 ACME 服务器的 CA 需要向 ACME 客户端提供 MAC 密钥和密钥标识符,使用 ACME 之外的一些机制。
§7.3.4
#### external_account.key_id

View File

@@ -108,6 +108,10 @@ It needs to be consistent with the server.
}
```
!!! warning ""
QUIC is not included by default, see [Installation](/#installation).
!!! warning "Difference from v2ray-core"
No additional encryption support:
@@ -115,9 +119,9 @@ It needs to be consistent with the server.
### gRPC
!!! warning ""
!!! note ""
gRPC is not included by default, see [Installation](/#installation).
standard gRPC has good compatibility but poor performance and is not included by default, see [Installation](/#installation).
```json
{

View File

@@ -107,6 +107,10 @@ HTTP 请求的额外标头。
}
```
!!! warning ""
默认安装不包含 QUIC, 参阅 [安装](/zh/#_2)。
!!! warning "与 v2ray-core 的区别"
没有额外的加密支持:
@@ -114,9 +118,9 @@ HTTP 请求的额外标头。
### gRPC
!!! warning ""
!!! note ""
默认安装不包含 gRPC, 参阅 [安装](/zh/#installation)。
默认安装不包含标准 gRPC (兼容性好,但性能较差), 参阅 [安装](/zh/#_2)。
```json
{

View File

@@ -0,0 +1,50 @@
# Development environment
#### For the documentation
##### Setup
You need to configure python3 and pip first.
```shell
pip install mkdocs-material mkdocs-static-i18n
```
##### Run the site locally
```shell
mkdocs serve
```
or
```shell
python3 -m mkdocs serve
```
#### For the project
By default you have the latest Go installed (currently 1.19), and added `GOPATH/bin` to the PATH environment variable.
##### Setup
```shell
make fmt_insalll
make lint_install
```
This installs the formatting and lint tools, which can be used via `make fmt` and `make lint`.
For ProtoBuffer changes, you also need `make proto_install` and `make proto`.
##### Build binary to the project directory
```shell
make
```
##### Install binary to GOPATH/bin
```shell
make install
```

View File

@@ -0,0 +1,17 @@
# Contributing to sing-box
An introduction to contributing to the sing-box project.
The sing-box project welcomes, and depends, on contributions from developers and users in the open source community.
Contributions can be made in a number of ways, a few examples are:
* Code patches via pull requests
* Documentation improvements
* Bug reports and patch reviews
### Reporting an Issue?
Please follow
the [issue template](https://github.com/SagerNet/sing-box/issues/new?assignees=&labels=&template=bug_report.yml) to
submit bugs. Always include **FULL** log content, especially if you don't understand the code that generates it.

View File

@@ -0,0 +1,78 @@
The sing-box uses the following projects which also need to be maintained:
#### sing
Link: [GitHub repository](https://github.com/SagerNet/sing)
As a base tool library, there are no dependencies other than `golang.org/x/sys`.
#### sing-dns
Link: [GitHub repository](https://github.com/SagerNet/sing-dns)
Handles DNS lookups and caching.
#### sing-tun
Link: [GitHub repository](https://github.com/SagerNet/sing-tun)
Handle Tun traffic forwarding, configure routing, monitor network and routing.
This library needs to periodically update its dependency gVisor (according to tags), including checking for changes to
the used parts of the code and updating its usage. If you are involved in maintenance, you also need to check that if it
works or contains memory leaks.
#### sing-shadowsocks
Link: [GitHub repository](https://github.com/SagerNet/sing-shadowsocks)
Provides Shadowsocks client and server
#### sing-vmess
Link: [GitHub repository](https://github.com/SagerNet/sing-vmess)
Provides VMess client and server
#### netlink
Link: [GitHub repository](https://github.com/SagerNet/netlink)
Fork of `vishvananda/netlink`, with some rule fixes.
The library needs to be updated with the upstream.
#### quic-go
Link: [GitHub repository](https://github.com/SagerNet/quic-go)
Fork of `lucas-clemente/quic-go` and `HyNetwork/quic-go`, contains quic flow control and other fixes used by Hysteria.
Since the author of Hysteria does not follow the upstream updates in time, and the provided fork needs to use replace,
we need to do this.
The library needs to be updated with the upstream.
#### certmagic
Link: [GitHub repository](https://github.com/SagerNet/certmagic)
Fork of `caddyserver/certmagic`
Since upstream uses `miekg/dns` and we use `x/net/dnsmessage`, we need to replace its DNS part with our own
implementation.
The library needs to be updated with the upstream.
#### smux
Link: [GitHub repository](https://github.com/SagerNet/smux)
Fork of `xtaci/smux`
Modify the code to support the writev it uses internally and unify the buffer pool, which prevents it from allocating
64k buffers for per connection and improves performance.
Upstream doesn't seem to be updated anymore, maybe a replacement is needed.
Note: while yamux is still actively maintained and better known, it seems to be less performant.

View File

@@ -2,7 +2,6 @@
* Linux & Systemd
* Git
* Go 1.18.5+
* C compiler environment
#### Install
@@ -10,6 +9,7 @@
```shell
git clone https://github.com/SagerNet/sing-box
cd sing-box
./release/local/install_go.sh # skip if you have go1.19 already installed
./release/local/install.sh
```

View File

@@ -2,7 +2,6 @@
* Linux & Systemd
* Git
* Go 1.18.5+
* C 编译器环境
#### 安装
@@ -10,10 +9,11 @@
```shell
git clone https://github.com/SagerNet/sing-box
cd sing-box
./release/local/install_go.sh # 如果已安装 go1.19 则跳过
./release/local/install.sh
```
Edit configuration file in `/usr/local/etc/sing-box/config.json`
编辑配置文件 `/usr/local/etc/sing-box/config.json`
```shell
./release/local/enable.sh

View File

@@ -24,8 +24,6 @@ v2ray-core 中的 "底层传输协议" 实际上是一些专有协议的组合
例如v2ray 社区将 v2ray 专有的 gRPC 协议称为 Trojan gRPC其实并不是一个 协议,在滥用 CDN 之外没有任何作用。
(译者注:由于实现错误, v2ray http2 传输层未能正确处理多路复用)
## Tun
#### 什么是 tun

View File

@@ -81,7 +81,7 @@
| Feature | clash-premium |
|-------------------------------------------|---------------|
| Full IPv6 support | X |
| Auto route on Linux/Windows/maxOS/Android | ✔ |
| Auto route on Linux/Windows/macOS/Android | ✔ |
| Embed windows driver | X |
| Custom address/mtu | X |
| Limit uid (Linux) in routing | X |

View File

@@ -1,3 +1,7 @@
---
description: Welcome to the wiki page for the sing-box project.
---
# Home
Welcome to the wiki page for the sing-box project.
@@ -18,16 +22,16 @@ Install with options:
go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@latest
```
| Build Tag | Description |
|------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `with_quic` | Build with QUIC support, see [QUIC and HTTP3 dns transports](./configuration/dns/server), [Naive inbound](./configuration/inbound/naive), [Hysteria Inbound](./configuration/inbound/hysteria) and [Hysteria Outbound](./configuration/outbound/hysteria). |
| `with_grpc` | Build with gRPC support, see [V2Ray Transport#gRPC](/configuration/shared/v2ray-transport#grpc). |
| `with_wireguard` | Build with WireGuard support, see [WireGuard outbound](./configuration/outbound/wireguard). |
| `with_acme` | Build with ACME TLS certificate issuer support, see [TLS](./configuration/shared/tls). |
| `with_clash_api` | Build with Clash API support, see [Experimental](./configuration/experimental#clash-api-fields). |
| `no_gvisor` | Build without gVisor Tun stack support, see [Tun inbound](./configuration/inbound/tun#stack). |
| `with_embedded_tor` (CGO required) | Build with embedded Tor support, see [Tor outbound](./configuration/outbound/tor). |
| `with_lwip` (CGO required) | Build with LWIP Tun stack support, see [Tun inbound](./configuration/inbound/tun#stack). |
| Build Tag | Description |
|------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `with_quic` | Build with QUIC support, see [QUIC and HTTP3 dns transports](./configuration/dns/server), [Naive inbound](./configuration/inbound/naive), [Hysteria Inbound](./configuration/inbound/hysteria), [Hysteria Outbound](./configuration/outbound/hysteria) and [V2Ray Transport#QUIC](./configuration/shared/v2ray-transport#quic). |
| `with_grpc` | Build with standard gRPC support, see [V2Ray Transport#gRPC](./configuration/shared/v2ray-transport#grpc). |
| `with_wireguard` | Build with WireGuard support, see [WireGuard outbound](./configuration/outbound/wireguard). |
| `with_acme` | Build with ACME TLS certificate issuer support, see [TLS](./configuration/shared/tls). |
| `with_clash_api` | Build with Clash API support, see [Experimental](./configuration/experimental#clash-api-fields). |
| `no_gvisor` | Build without gVisor Tun stack support, see [Tun inbound](./configuration/inbound/tun#stack). |
| `with_embedded_tor` (CGO required) | Build with embedded Tor support, see [Tor outbound](./configuration/outbound/tor). |
| `with_lwip` (CGO required) | Build with LWIP Tun stack support, see [Tun inbound](./configuration/inbound/tun#stack). |
The binary is built under $GOPATH/bin

View File

@@ -1,3 +1,7 @@
---
description: 欢迎来到该 sing-box 项目的文档页。
---
# 开始
欢迎来到该 sing-box 项目的文档页。
@@ -18,16 +22,16 @@ go install -v github.com/sagernet/sing-box/cmd/sing-box@latest
go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@latest
```
| 构建标志 | 描述 |
|------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `with_quic` | 启用 QUIC 支持,参阅 [QUIC 和 HTTP3 DNS 传输层](./configuration/dns/server)[Naive 入站](./configuration/inbound/naive)[Hysteria 入站](./configuration/inbound/hysteria)[Hysteria 出站](./configuration/outbound/hysteria)。 |
| `with_grpc` | 启用 gRPC 支持,参阅 [V2Ray 传输层#gRPC](/configuration/shared/v2ray-transport#grpc)。 |
| `with_wireguard` | 启用 WireGuard 支持,参阅 [WireGuard 出站](./configuration/outbound/wireguard)。 |
| `with_acme` | 启用 ACME TLS 证书签发支持,参阅 [TLS](./configuration/shared/tls)。 |
| `with_clash_api` | 启用 Clash api 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
| `no_gvisor` | 禁用 gVisor Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
| `with_embedded_tor` (需要 CGO) | 启用 嵌入式 Tor 支持,参阅 [Tor 出站](./configuration/outbound/tor)。 |
| `with_lwip` (需要 CGO) | 启用 LWIP Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
| 构建标志 | 描述 |
|------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `with_quic` | 启用 QUIC 支持,参阅 [QUIC 和 HTTP3 DNS 传输层](./configuration/dns/server)[Naive 入站](./configuration/inbound/naive)[Hysteria 入站](./configuration/inbound/hysteria)[Hysteria 出站](./configuration/outbound/hysteria) 和 [V2Ray 传输层#QUIC](./configuration/shared/v2ray-transport#quic)。 |
| `with_grpc` | 启用标准 gRPC 支持,参阅 [V2Ray 传输层#gRPC](./configuration/shared/v2ray-transport#grpc)。 |
| `with_wireguard` | 启用 WireGuard 支持,参阅 [WireGuard 出站](./configuration/outbound/wireguard)。 |
| `with_acme` | 启用 ACME TLS 证书签发支持,参阅 [TLS](./configuration/shared/tls)。 |
| `with_clash_api` | 启用 Clash api 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
| `no_gvisor` | 禁用 gVisor Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
| `with_embedded_tor` (需要 CGO) | 启用 嵌入式 Tor 支持,参阅 [Tor 出站](./configuration/outbound/tor)。 |
| `with_lwip` (需要 CGO) | 启用 LWIP Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
二进制文件将被构建在 `$GOPATH/bin` 下。
@@ -45,11 +49,18 @@ sing-box version
## 授权
```
版权所有 (C) 2022 by nekohasekai <contact-sagernet@sekai.icu>
Copyright (C) 2022 by nekohasekai <contact-sagernet@sekai.icu>
该程序是免费软件:您可以重新分发和 / 或修改根据 GNU 通用公共许可证的条款,由自由软件基金会,许可证的第 3 版,或(由您选择)任何更高版本。
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
分发这个程序是希望它有用,但没有任何保证; 甚至没有暗示的保证适销性或特定用途的适用性。 见 GNU 通用公共许可证以获取更多详细信息。
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
您应该已经收到一份 GNU 通用公共许可证的副本连同这个程序。 如果没有,请参阅 <http://www.gnu.org/licenses/>。
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
```

View File

@@ -1,165 +0,0 @@
package daemon
import (
"io"
"os"
"path/filepath"
"runtime"
"strings"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/rw"
"github.com/kardianos/service"
C "github.com/sagernet/sing-box/constant"
)
const (
DefaultDaemonName = "sing-box-daemon"
DefaultDaemonPort = 9091
)
var defaultDaemonOptions = Options{
Listen: "127.0.0.1",
ListenPort: DefaultDaemonPort,
WorkingDirectory: workingDirectory(),
}
func workingDirectory() string {
switch runtime.GOOS {
case "linux":
return filepath.Join("/usr/local/lib", DefaultDaemonName)
default:
configDir, err := os.UserConfigDir()
if err == nil {
return filepath.Join(configDir, DefaultDaemonName)
} else {
return DefaultDaemonName
}
}
}
const systemdScript = `[Unit]
Description=sing-box service
Documentation=https://sing-box.sagernet.org
After=network.target nss-lookup.target
[Service]
User=root
ExecStart={{.Path|cmdEscape}}{{range .Arguments}} {{.|cmd}}{{end}}
WorkingDirectory={{.WorkingDirectory|cmdEscape}}
Restart=on-failure
RestartSec=10s
LimitNOFILE=infinity
[Install]
WantedBy=multi-user.target`
type Daemon struct {
service service.Service
workingDirectory string
executable string
}
func New() (*Daemon, error) {
daemonInterface := NewInterface(defaultDaemonOptions)
executable := filepath.Join(defaultDaemonOptions.WorkingDirectory, "sing-box")
if C.IsWindows {
executable += ".exe"
}
daemonService, err := service.New(daemonInterface, &service.Config{
Name: DefaultDaemonName,
Description: "The universal proxy platform.",
Arguments: []string{"daemon", "run"},
Executable: executable,
Option: service.KeyValue{
"SystemdScript": systemdScript,
},
})
if err != nil {
return nil, E.New(strings.ToLower(err.Error()))
}
return &Daemon{
service: daemonService,
workingDirectory: defaultDaemonOptions.WorkingDirectory,
executable: executable,
}, nil
}
func (d *Daemon) Install() error {
_, err := d.service.Status()
if err != service.ErrNotInstalled {
d.service.Stop()
err = d.service.Uninstall()
if err != nil {
return err
}
}
executablePath, err := os.Executable()
if err != nil {
return err
}
if !rw.FileExists(d.workingDirectory) {
err = os.MkdirAll(d.workingDirectory, 0o755)
if err != nil {
return err
}
}
outputFile, err := os.OpenFile(d.executable, os.O_CREATE|os.O_WRONLY, 0o755)
if err != nil {
return err
}
inputFile, err := os.Open(executablePath)
if err != nil {
outputFile.Close()
return err
}
_, err = io.Copy(outputFile, inputFile)
inputFile.Close()
outputFile.Close()
if err != nil {
return err
}
err = d.service.Install()
if err != nil {
return err
}
return d.service.Start()
}
func (d *Daemon) Uninstall() error {
_, err := d.service.Status()
if err != service.ErrNotInstalled {
d.service.Stop()
err = d.service.Uninstall()
if err != nil {
return err
}
}
return os.RemoveAll(d.workingDirectory)
}
func (d *Daemon) Run() error {
d.chdir()
return d.service.Run()
}
func (d *Daemon) chdir() error {
executable, err := os.Executable()
if err != nil {
return err
}
return os.Chdir(filepath.Dir(executable))
}
func (d *Daemon) Start() error {
return d.service.Start()
}
func (d *Daemon) Stop() error {
return d.service.Stop()
}
func (d *Daemon) Restart() error {
return d.service.Restart()
}

View File

@@ -1,58 +0,0 @@
package daemon
import (
"context"
"os"
"sync"
"github.com/sagernet/sing-box"
"github.com/sagernet/sing-box/option"
)
type Instance struct {
access sync.Mutex
boxInstance *box.Box
boxCancel context.CancelFunc
}
func (i *Instance) Running() bool {
i.access.Lock()
defer i.access.Unlock()
return i.boxInstance != nil
}
func (i *Instance) Start(options option.Options) error {
i.access.Lock()
defer i.access.Unlock()
if i.boxInstance != nil {
i.boxCancel()
i.boxInstance.Close()
}
ctx, cancel := context.WithCancel(context.Background())
instance, err := box.New(ctx, options)
if err != nil {
cancel()
return err
}
err = instance.Start()
if err != nil {
cancel()
return err
}
i.boxInstance = instance
i.boxCancel = cancel
return nil
}
func (i *Instance) Close() error {
i.access.Lock()
defer i.access.Unlock()
if i.boxInstance == nil {
return os.ErrClosed
}
i.boxCancel()
err := i.boxInstance.Close()
i.boxInstance = nil
i.boxCancel = nil
return err
}

View File

@@ -1,20 +0,0 @@
package daemon
import "github.com/kardianos/service"
type Interface struct {
server *Server
}
func NewInterface(options Options) *Interface {
return &Interface{NewServer(options)}
}
func (d *Interface) Start(_ service.Service) error {
return d.server.Start()
}
func (d *Interface) Stop(_ service.Service) error {
d.server.Close()
return nil
}

View File

@@ -1,147 +0,0 @@
package daemon
import (
"io"
"net"
"net/http"
"net/http/pprof"
"strings"
"github.com/sagernet/sing-box/common/json"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
"github.com/go-chi/chi/v5"
"github.com/go-chi/cors"
"github.com/go-chi/render"
"github.com/gorilla/websocket"
)
type Options struct {
Listen string `json:"listen"`
ListenPort uint16 `json:"listen_port"`
Secret string `json:"secret"`
WorkingDirectory string `json:"working_directory"`
}
type Server struct {
options Options
httpServer *http.Server
instance Instance
}
func NewServer(options Options) *Server {
return &Server{
options: options,
}
}
func (s *Server) Start() error {
tcpConn, err := net.Listen("tcp", net.JoinHostPort(s.options.Listen, F.ToString(s.options.ListenPort)))
if err != nil {
return err
}
router := chi.NewRouter()
router.Use(cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE"},
AllowedHeaders: []string{"Content-Type", "Authorization"},
MaxAge: 300,
}).Handler)
if s.options.Secret != "" {
router.Use(s.authentication)
}
router.Get("/ping", s.ping)
router.Get("/status", s.status)
router.Post("/run", s.run)
router.Get("/stop", s.stop)
router.Route("/debug/pprof", func(r chi.Router) {
r.HandleFunc("/", pprof.Index)
r.HandleFunc("/cmdline", pprof.Cmdline)
r.HandleFunc("/profile", pprof.Profile)
r.HandleFunc("/symbol", pprof.Symbol)
r.HandleFunc("/trace", pprof.Trace)
})
httpServer := &http.Server{
Handler: router,
}
go httpServer.Serve(tcpConn)
s.httpServer = httpServer
return nil
}
func (s *Server) authentication(next http.Handler) http.Handler {
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
if websocket.IsWebSocketUpgrade(request) && request.URL.Query().Get("token") != "" {
token := request.URL.Query().Get("token")
if token != s.options.Secret {
render.Status(request, http.StatusUnauthorized)
return
}
next.ServeHTTP(writer, request)
return
}
header := request.Header.Get("Authorization")
bearer, token, found := strings.Cut(header, " ")
hasInvalidHeader := bearer != "Bearer"
hasInvalidSecret := !found || token != s.options.Secret
if hasInvalidHeader || hasInvalidSecret {
render.Status(request, http.StatusUnauthorized)
return
}
next.ServeHTTP(writer, request)
})
}
func (s *Server) Close() error {
return common.Close(
common.PtrOrNil(s.httpServer),
&s.instance,
)
}
func (s *Server) ping(writer http.ResponseWriter, request *http.Request) {
render.PlainText(writer, request, "pong")
}
type StatusResponse struct {
Running bool `json:"running"`
}
func (s *Server) status(writer http.ResponseWriter, request *http.Request) {
render.JSON(writer, request, StatusResponse{
Running: s.instance.Running(),
})
}
func (s *Server) run(writer http.ResponseWriter, request *http.Request) {
err := s.run0(request)
if err != nil {
log.Warn(err)
render.Status(request, http.StatusBadRequest)
render.PlainText(writer, request, err.Error())
return
}
writer.WriteHeader(http.StatusNoContent)
}
func (s *Server) run0(request *http.Request) error {
configContent, err := io.ReadAll(request.Body)
if err != nil {
return E.Cause(err, "read config")
}
var options option.Options
err = json.Unmarshal(configContent, &options)
if err != nil {
return E.Cause(err, "decode config")
}
return s.instance.Start(options)
}
func (s *Server) stop(writer http.ResponseWriter, request *http.Request) {
s.instance.Close()
writer.WriteHeader(http.StatusNoContent)
}

25
go.mod
View File

@@ -14,30 +14,29 @@ require (
github.com/gofrs/uuid v4.2.0+incompatible
github.com/gorilla/websocket v1.5.0
github.com/hashicorp/yamux v0.1.1
github.com/kardianos/service v1.2.1
github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/mholt/acmez v1.0.4
github.com/oschwald/maxminddb-golang v1.10.0
github.com/pires/go-proxyproto v0.6.2
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a
github.com/sagernet/netlink v0.0.0-20220820041223-3cd8365d17ac
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb
github.com/sagernet/sing v0.0.0-20220824062950-7bfd820739a8
github.com/sagernet/sing v0.0.0-20220826124916-d4ba8fdfac88
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
github.com/sagernet/sing-tun v0.0.0-20220824105617-e5c59fc756a6
github.com/sagernet/sing-vmess v0.0.0-20220811135656-4f3f07acf9c4
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83
github.com/sagernet/sing-vmess v0.0.0-20220827032426-01665c9c4e31
github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939
github.com/spf13/cobra v1.5.0
github.com/stretchr/testify v1.8.0
go.uber.org/atomic v1.10.0
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8
golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64
golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478
google.golang.org/grpc v1.48.0
google.golang.org/grpc v1.49.0
google.golang.org/protobuf v1.28.1
gvisor.dev/gvisor v0.0.0-20220812001733-b5c0f23893fb
gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a
)
require (
@@ -58,16 +57,16 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // indirect
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.22.0 // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
golang.org/x/tools v0.1.10 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect

58
go.sum
View File

@@ -9,15 +9,10 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
@@ -34,7 +29,6 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -77,7 +71,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
@@ -88,8 +81,6 @@ github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbg
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/kardianos/service v1.2.1 h1:AYndMsehS+ywIS6RB9KOlcXzteWUzxgMgBymJD7+BYk=
github.com/kardianos/service v1.2.1/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0=
github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
@@ -138,22 +129,22 @@ github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a h1:SE3Xn4GOQ+kx
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a/go.mod h1:Q+ZXyesnkjV5B70B1ixk65ecKrlJ2jz0atv3fPKsVVo=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
github.com/sagernet/netlink v0.0.0-20220820041223-3cd8365d17ac h1:I03d2HNy5f3INRZfsvuoLhz0h3qqsDLbKSw0EsYxQxI=
github.com/sagernet/netlink v0.0.0-20220820041223-3cd8365d17ac/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17 h1:zvm6IrIgo4rLizJCHkH+SWUBhm+jyjjozX031QdAlj8=
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb h1:wc0yQ+SBn4TaTYRwpwvEm3nc4eRdxk6vtRbouLVZAzk=
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4=
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.0.0-20220824062950-7bfd820739a8 h1:kHsinrGrMjEh5KUXC/MPCS+Uy3Z3XO/cMhC8xJtABE8=
github.com/sagernet/sing v0.0.0-20220824062950-7bfd820739a8/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
github.com/sagernet/sing v0.0.0-20220826124916-d4ba8fdfac88 h1:wxUQfVBqiUtAemytzP9mNjAkSiI0nVsRZBQvCLP8r5g=
github.com/sagernet/sing v0.0.0-20220826124916-d4ba8fdfac88/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFxUX+xJCWMPPFZCn2GC/uLrBjTSr1vHY=
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM=
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
github.com/sagernet/sing-tun v0.0.0-20220824105617-e5c59fc756a6 h1:C0uNMDrjYribl4Pu41Au9UeQROIOeMWaDd7eSUIQ9gQ=
github.com/sagernet/sing-tun v0.0.0-20220824105617-e5c59fc756a6/go.mod h1:zMKRFCEoO6Jp5Yxb2NUTqc+SvAtNVAmzfwArAheJy5g=
github.com/sagernet/sing-vmess v0.0.0-20220811135656-4f3f07acf9c4 h1:2hLETh97+S4WnfMR27XyC7QVU1SH7FTNoCznP229YJU=
github.com/sagernet/sing-vmess v0.0.0-20220811135656-4f3f07acf9c4/go.mod h1:82O6gzbxLha/W/jxSVQbsqf2lVdRTjMIgyLug0lpJps=
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83 h1:SoWiHYuOCVedqA7T/CJSZUUrcPGKQb2wFKEq8DphiAI=
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83/go.mod h1:76r07HS1WRcEI4mE9pFsohfTBUt1j/G9Avz6DaOP3VU=
github.com/sagernet/sing-vmess v0.0.0-20220827032426-01665c9c4e31 h1:FAsJsVwpPcoITcj6/9JxRKxy8n3bIKLqKmDGVzmfeOo=
github.com/sagernet/sing-vmess v0.0.0-20220827032426-01665c9c4e31/go.mod h1:82O6gzbxLha/W/jxSVQbsqf2lVdRTjMIgyLug0lpJps=
github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939 h1:pB1Dh1NbwVrLhQhotr4O4Hs3yhiBzmg3AvnUyYjL4x4=
github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
@@ -183,13 +174,15 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
go.uber.org/zap v1.22.0 h1:Zcye5DUgBloQ9BaT4qc9BnjOFog5TvBSAGkJ3Nf70c0=
go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U=
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d h1:ggxwEf5eu0l8v+87VhX1czFh8zJul3hK16Gmruxn7hw=
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 h1:GIAS/yBem/gq2MUqgNIzUHW7cJMmx3TGZOrnyYaNQ6c=
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
@@ -201,8 +194,8 @@ golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPI
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -219,8 +212,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c h1:JVAXQ10yGGVbSyoer5VILysz6YKjdNT2bsvlayjqhes=
golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -241,10 +234,8 @@ golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -253,8 +244,8 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U=
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -277,12 +268,11 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f h1:OKYpQQVE3DKSc3r3zHVzq46vq5YH7x8xpR3/k9ixmUg=
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
@@ -303,8 +293,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw=
google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -336,8 +326,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gvisor.dev/gvisor v0.0.0-20220812001733-b5c0f23893fb h1:Z7S1dQX1RX+Tq55/Jq5kJQnPBdxA19ZmhgCmFjyK+yA=
gvisor.dev/gvisor v0.0.0-20220812001733-b5c0f23893fb/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a h1:W1h3JsEzYWg7eD4908iHv49p7AOx7JPKsoh/fsxgylM=
gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=

View File

@@ -187,7 +187,6 @@ func (a *myInboundAdapter) createMetadata(conn net.Conn, metadata adapter.Inboun
metadata.SniffEnabled = a.listenOptions.SniffEnabled
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
metadata.Network = N.NetworkTCP
if !metadata.Source.IsValid() {
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr())
}
@@ -242,7 +241,6 @@ func (a *myInboundAdapter) loopUDPIn() {
metadata.SniffEnabled = a.listenOptions.SniffEnabled
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
metadata.Network = N.NetworkUDP
metadata.Source = M.SocksaddrFromNetIP(addr)
metadata.OriginDestination = a.udpAddr
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
@@ -275,7 +273,6 @@ func (a *myInboundAdapter) loopUDPOOBIn() {
metadata.SniffEnabled = a.listenOptions.SniffEnabled
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
metadata.Network = N.NetworkUDP
metadata.Source = M.SocksaddrFromNetIP(addr)
metadata.OriginDestination = a.udpAddr
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
@@ -302,7 +299,6 @@ func (a *myInboundAdapter) loopUDPInThreadSafe() {
metadata.SniffEnabled = a.listenOptions.SniffEnabled
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
metadata.Network = N.NetworkUDP
metadata.Source = M.SocksaddrFromNetIP(addr)
metadata.OriginDestination = a.udpAddr
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
@@ -331,7 +327,6 @@ func (a *myInboundAdapter) loopUDPOOBInThreadSafe() {
metadata.SniffEnabled = a.listenOptions.SniffEnabled
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
metadata.Network = N.NetworkUDP
metadata.Source = M.SocksaddrFromNetIP(addr)
metadata.OriginDestination = a.udpAddr
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)

View File

@@ -267,7 +267,6 @@ func (h *Hysteria) acceptStream(ctx context.Context, conn quic.Connection, strea
metadata.Destination = M.ParseSocksaddrHostPort(request.Host, request.Port)
if !request.UDP {
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
metadata.Network = N.NetworkTCP
return h.router.RouteConnection(ctx, hysteria.NewConn(stream, metadata.Destination), metadata)
} else {
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
@@ -278,7 +277,6 @@ func (h *Hysteria) acceptStream(ctx context.Context, conn quic.Connection, strea
h.udpSessions[id] = nCh
h.udpSessionId += 1
h.udpAccess.Unlock()
metadata.Network = N.NetworkUDP
packetConn := hysteria.NewPacketConn(conn, stream, id, metadata.Destination, nCh, common.Closer(func() error {
h.udpAccess.Lock()
if ch, ok := h.udpSessions[id]; ok {

View File

@@ -24,11 +24,12 @@ var _ adapter.Inbound = (*Trojan)(nil)
type Trojan struct {
myInboundAdapter
service *trojan.Service[int]
users []option.TrojanUser
tlsConfig *TLSConfig
fallbackAddr M.Socksaddr
transport adapter.V2RayServerTransport
service *trojan.Service[int]
users []option.TrojanUser
tlsConfig *TLSConfig
fallbackAddr M.Socksaddr
fallbackAddrTLSNextProto map[string]M.Socksaddr
transport adapter.V2RayServerTransport
}
func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TrojanInboundOptions) (*Trojan, error) {
@@ -44,9 +45,35 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
},
users: options.Users,
}
if options.TLS != nil {
tlsConfig, err := NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}
inbound.tlsConfig = tlsConfig
}
var fallbackHandler N.TCPConnectionHandler
if options.Fallback != nil && options.Fallback.Server != "" {
inbound.fallbackAddr = options.Fallback.Build()
if options.Fallback != nil && options.Fallback.Server != "" || len(options.FallbackForALPN) > 0 {
if options.Fallback != nil && options.Fallback.Server != "" {
inbound.fallbackAddr = options.Fallback.Build()
if !inbound.fallbackAddr.IsValid() {
return nil, E.New("invalid fallback address: ", inbound.fallbackAddr)
}
}
if len(options.FallbackForALPN) > 0 {
if inbound.tlsConfig == nil {
return nil, E.New("fallback for ALPN is not supported without TLS")
}
fallbackAddrNextProto := make(map[string]M.Socksaddr)
for nextProto, destination := range options.FallbackForALPN {
fallbackAddr := destination.Build()
if !fallbackAddr.IsValid() {
return nil, E.New("invalid fallback address for ALPN ", nextProto, ": ", fallbackAddr)
}
fallbackAddrNextProto[nextProto] = fallbackAddr
}
inbound.fallbackAddrTLSNextProto = fallbackAddrNextProto
}
fallbackHandler = adapter.NewUpstreamContextHandler(inbound.fallbackConnection, nil, nil)
}
service := trojan.NewService[int](adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound), fallbackHandler)
@@ -58,13 +85,6 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
if err != nil {
return nil, err
}
if options.TLS != nil {
tlsConfig, err := NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}
inbound.tlsConfig = tlsConfig
}
if options.Transport != nil {
var tlsConfig *tls.Config
if inbound.tlsConfig != nil {
@@ -153,8 +173,22 @@ func (h *Trojan) newConnection(ctx context.Context, conn net.Conn, metadata adap
}
func (h *Trojan) fallbackConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
h.logger.InfoContext(ctx, "fallback connection to ", h.fallbackAddr)
metadata.Destination = h.fallbackAddr
var fallbackAddr M.Socksaddr
if len(h.fallbackAddrTLSNextProto) > 0 {
if tlsConn, loaded := common.Cast[*tls.Conn](conn); loaded {
connectionState := tlsConn.ConnectionState()
if connectionState.NegotiatedProtocol != "" {
if fallbackAddr, loaded = h.fallbackAddrTLSNextProto[connectionState.NegotiatedProtocol]; !loaded {
return E.New("fallback disabled for ALPN: ", connectionState.NegotiatedProtocol)
}
}
}
}
if !fallbackAddr.IsValid() {
fallbackAddr = h.fallbackAddr
}
h.logger.InfoContext(ctx, "fallback connection to ", fallbackAddr)
metadata.Destination = fallbackAddr
return h.router.RouteConnection(ctx, conn, metadata)
}

View File

@@ -80,6 +80,7 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
Inet4Address: options.Inet4Address.Build(),
Inet6Address: options.Inet6Address.Build(),
AutoRoute: options.AutoRoute,
StrictRoute: options.StrictRoute,
IncludeUID: includeUID,
ExcludeUID: excludeUID,
IncludeAndroidUser: options.IncludeAndroidUser,
@@ -165,7 +166,6 @@ func (t *Tun) NewConnection(ctx context.Context, conn net.Conn, upstreamMetadata
var metadata adapter.InboundContext
metadata.Inbound = t.tag
metadata.InboundType = C.TypeTun
metadata.Network = N.NetworkTCP
metadata.Source = upstreamMetadata.Source
metadata.Destination = upstreamMetadata.Destination
metadata.SniffEnabled = t.inboundOptions.SniffEnabled
@@ -188,7 +188,6 @@ func (t *Tun) NewPacketConnection(ctx context.Context, conn N.PacketConn, upstre
var metadata adapter.InboundContext
metadata.Inbound = t.tag
metadata.InboundType = C.TypeTun
metadata.Network = N.NetworkUDP
metadata.Source = upstreamMetadata.Source
metadata.Destination = upstreamMetadata.Destination
metadata.SniffEnabled = t.inboundOptions.SniffEnabled

View File

@@ -12,10 +12,12 @@ import (
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/v2ray"
"github.com/sagernet/sing-vmess"
"github.com/sagernet/sing-vmess/packetaddr"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
@@ -161,6 +163,12 @@ func (h *VMess) newPacketConnection(ctx context.Context, conn N.PacketConn, meta
} else {
metadata.User = user
}
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
if metadata.Destination.Fqdn == packetaddr.SeqPacketMagicAddress {
metadata.Destination = M.Socksaddr{}
conn = packetaddr.NewConn(conn.(vmess.PacketConn), metadata.Destination)
h.logger.InfoContext(ctx, "[", user, "] inbound packet addr connection")
} else {
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
}
return h.router.RoutePacketConnection(ctx, conn, metadata)
}

View File

@@ -49,7 +49,7 @@ nav:
- Route Rule: configuration/route/rule.md
- Protocol Sniff: configuration/route/sniff.md
- Experimental:
- configuration/experimental/index.md
- configuration/experimental/index.md
- Shared:
- TLS: configuration/shared/tls.md
- Multiplex: configuration/shared/multiplex.md
@@ -93,6 +93,11 @@ nav:
- Shadowsocks Client: examples/ss-client.md
- Shadowsocks Tun: examples/ss-tun.md
- DNS Hijack: examples/dns-hijack.md
- Contributing:
- contributing/index.md
- Developing:
- Environment: contributing/environment.md
- Sub projects: contributing/sub-projects.md
markdown_extensions:
- pymdownx.inlinehilite
- pymdownx.snippets

View File

@@ -100,14 +100,14 @@ func (h *Outbound) UnmarshalJSON(bytes []byte) error {
}
type DialerOptions struct {
Detour string `json:"detour,omitempty"`
BindInterface string `json:"bind_interface,omitempty"`
BindAddress ListenAddress `json:"bind_address,omitempty"`
ProtectPath string `json:"protect_path,omitempty"`
RoutingMark int `json:"routing_mark,omitempty"`
ReuseAddr bool `json:"reuse_addr,omitempty"`
ConnectTimeout Duration `json:"connect_timeout,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
Detour string `json:"detour,omitempty"`
BindInterface string `json:"bind_interface,omitempty"`
BindAddress *ListenAddress `json:"bind_address,omitempty"`
ProtectPath string `json:"protect_path,omitempty"`
RoutingMark int `json:"routing_mark,omitempty"`
ReuseAddr bool `json:"reuse_addr,omitempty"`
ConnectTimeout Duration `json:"connect_timeout,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
}
type OutboundDialerOptions struct {

View File

@@ -2,10 +2,11 @@ package option
type TrojanInboundOptions struct {
ListenOptions
Users []TrojanUser `json:"users,omitempty"`
TLS *InboundTLSOptions `json:"tls,omitempty"`
Fallback *ServerOptions `json:"fallback,omitempty"`
Transport *V2RayTransportOptions `json:"transport,omitempty"`
Users []TrojanUser `json:"users,omitempty"`
TLS *InboundTLSOptions `json:"tls,omitempty"`
Fallback *ServerOptions `json:"fallback,omitempty"`
FallbackForALPN map[string]*ServerOptions `json:"fallback_for_alpn,omitempty"`
Transport *V2RayTransportOptions `json:"transport,omitempty"`
}
type TrojanUser struct {

View File

@@ -6,6 +6,7 @@ type TunInboundOptions struct {
Inet4Address *ListenPrefix `json:"inet4_address,omitempty"`
Inet6Address *ListenPrefix `json:"inet6_address,omitempty"`
AutoRoute bool `json:"auto_route,omitempty"`
StrictRoute bool `json:"strict_route,omitempty"`
IncludeUID Listable[uint32] `json:"include_uid,omitempty"`
IncludeUIDRange Listable[string] `json:"include_uid_range,omitempty"`
ExcludeUID Listable[uint32] `json:"exclude_uid,omitempty"`

View File

@@ -16,7 +16,7 @@ type ListenAddress netip.Addr
func (a ListenAddress) MarshalJSON() ([]byte, error) {
addr := netip.Addr(a)
if !addr.IsValid() {
return json.Marshal("")
return nil, nil
}
return json.Marshal(addr.String())
}
@@ -35,6 +35,10 @@ func (a *ListenAddress) UnmarshalJSON(content []byte) error {
return nil
}
func (a ListenAddress) Build() netip.Addr {
return (netip.Addr)(a)
}
type NetworkList string
func (v *NetworkList) UnmarshalJSON(content []byte) error {

View File

@@ -78,4 +78,5 @@ type V2RayQUICOptions struct{}
type V2RayGRPCOptions struct {
ServiceName string `json:"service_name,omitempty"`
ForceLite bool `json:"-"` // for test
}

View File

@@ -23,6 +23,7 @@ type VMessOutboundOptions struct {
AuthenticatedLength bool `json:"authenticated_length,omitempty"`
Network NetworkList `json:"network,omitempty"`
TLS *OutboundTLSOptions `json:"tls,omitempty"`
PacketAddr bool `json:"packet_addr,omitempty"`
Multiplex *MultiplexOptions `json:"multiplex,omitempty"`
Transport *V2RayTransportOptions `json:"transport,omitempty"`
}

View File

@@ -3,13 +3,13 @@ package outbound
import (
"context"
"encoding/binary"
"io"
"net"
"os"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/canceler"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-dns"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
@@ -47,53 +47,60 @@ func (d *DNS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.Pa
func (d *DNS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
defer conn.Close()
ctx = adapter.WithContext(ctx, &metadata)
_buffer := buf.StackNewSize(1024)
for {
err := d.handleConnection(ctx, conn, metadata)
if err != nil {
return err
}
}
}
func (d *DNS) handleConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
var queryLength uint16
err := binary.Read(conn, binary.BigEndian, &queryLength)
if err != nil {
return err
}
if queryLength == 0 {
return dns.RCodeFormatError
}
_buffer := buf.StackNewSize(int(queryLength))
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
for {
var queryLength uint16
err := binary.Read(conn, binary.BigEndian, &queryLength)
if err != nil {
return err
}
if queryLength > 1024 {
return io.ErrShortBuffer
}
buffer.FullReset()
_, err = buffer.ReadFullFrom(conn, int(queryLength))
if err != nil {
return err
}
var message dnsmessage.Message
err = message.Unpack(buffer.Bytes())
if err != nil {
return err
}
if len(message.Questions) > 0 {
question := message.Questions[0]
metadata.Domain = string(question.Name.Data[:question.Name.Length-1])
}
go func() error {
response, err := d.router.Exchange(ctx, &message)
if err != nil {
return err
}
_responseBuffer := buf.StackNewPacket()
defer common.KeepAlive(_responseBuffer)
responseBuffer := common.Dup(_responseBuffer)
defer responseBuffer.Release()
responseBuffer.Resize(2, 0)
n, err := response.AppendPack(responseBuffer.Index(0))
if err != nil {
return err
}
responseBuffer.Truncate(len(n))
binary.BigEndian.PutUint16(responseBuffer.ExtendHeader(2), uint16(len(n)))
_, err = conn.Write(responseBuffer.Bytes())
return err
}()
_, err = buffer.ReadFullFrom(conn, int(queryLength))
if err != nil {
return err
}
var message dnsmessage.Message
err = message.Unpack(buffer.Bytes())
if err != nil {
return err
}
if len(message.Questions) > 0 {
question := message.Questions[0]
metadata.Domain = string(question.Name.Data[:question.Name.Length-1])
}
go func() error {
response, err := d.router.Exchange(ctx, &message)
if err != nil {
return err
}
_responseBuffer := buf.StackNewPacket()
defer common.KeepAlive(_responseBuffer)
responseBuffer := common.Dup(_responseBuffer)
defer responseBuffer.Release()
responseBuffer.Resize(2, 0)
n, err := response.AppendPack(responseBuffer.Index(0))
if err != nil {
return err
}
responseBuffer.Truncate(len(n))
binary.BigEndian.PutUint16(responseBuffer.ExtendHeader(2), uint16(len(n)))
_, err = conn.Write(responseBuffer.Bytes())
return err
}()
return nil
}
func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
@@ -103,7 +110,7 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada
var group task.Group
group.Append0(func(ctx context.Context) error {
defer cancel()
_buffer := buf.StackNewSize(1024)
_buffer := buf.StackNewSize(dns.FixedPacketSize)
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()

View File

@@ -13,6 +13,7 @@ import (
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/v2ray"
"github.com/sagernet/sing-vmess"
"github.com/sagernet/sing-vmess/packetaddr"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
@@ -29,20 +30,10 @@ type VMess struct {
multiplexDialer N.Dialer
tlsConfig *tls.Config
transport adapter.V2RayClientTransport
packetAddr bool
}
func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VMessOutboundOptions) (*VMess, error) {
var clientOptions []vmess.ClientOption
if options.GlobalPadding {
clientOptions = append(clientOptions, vmess.ClientWithGlobalPadding())
}
if options.AuthenticatedLength {
clientOptions = append(clientOptions, vmess.ClientWithAuthenticatedLength())
}
client, err := vmess.NewClient(options.UUID, options.Security, options.AlterId, clientOptions...)
if err != nil {
return nil, err
}
outbound := &VMess{
myOutboundAdapter: myOutboundAdapter{
protocol: C.TypeVMess,
@@ -52,9 +43,9 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
tag: tag,
},
dialer: dialer.NewOutbound(router, options.OutboundDialerOptions),
client: client,
serverAddr: options.ServerOptions.Build(),
}
var err error
if options.TLS != nil {
outbound.tlsConfig, err = dialer.TLSConfig(options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil {
@@ -71,6 +62,28 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
if err != nil {
return nil, err
}
if outbound.multiplexDialer == nil && options.PacketAddr {
outbound.packetAddr = true
}
var clientOptions []vmess.ClientOption
if options.GlobalPadding {
clientOptions = append(clientOptions, vmess.ClientWithGlobalPadding())
}
if options.AuthenticatedLength {
clientOptions = append(clientOptions, vmess.ClientWithAuthenticatedLength())
}
security := options.Security
if security == "" {
security = "auto"
}
if security == "auto" && outbound.tlsConfig != nil {
security = "zero"
}
client, err := vmess.NewClient(options.UUID, security, options.AlterId, clientOptions...)
if err != nil {
return nil, err
}
outbound.client = client
return outbound, nil
}
@@ -146,9 +159,25 @@ func (h *vmessDialer) DialContext(ctx context.Context, network string, destinati
}
func (h *vmessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
conn, err := h.DialContext(ctx, N.NetworkUDP, destination)
ctx, metadata := adapter.AppendContext(ctx)
metadata.Outbound = h.tag
metadata.Destination = destination
var conn net.Conn
var err error
if h.transport != nil {
conn, err = h.transport.DialContext(ctx)
} else {
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
if err == nil && h.tlsConfig != nil {
conn, err = dialer.TLSClient(ctx, conn, h.tlsConfig)
}
}
if err != nil {
return nil, err
}
return conn.(vmess.PacketConn), nil
if h.packetAddr {
return packetaddr.NewConn(h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}), destination), nil
} else {
return h.client.DialEarlyPacketConn(conn, destination), nil
}
}

View File

@@ -2,6 +2,10 @@
set -e -o pipefail
if [ -d /usr/local/go ]; then
export PATH="$PATH:/usr/local/go/bin"
fi
DIR=$(dirname "$0")
PROJECT=$DIR/../..

View File

@@ -2,6 +2,10 @@
set -e -o pipefail
if [ -d /usr/local/go ]; then
export PATH="$PATH:/usr/local/go/bin"
fi
DIR=$(dirname "$0")
PROJECT=$DIR/../..

7
release/local/install_go.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -e -o pipefail
curl -o go.tar.gz https://go.dev/dl/go1.19.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go.tar.gz
rm go.tar.gz

View File

@@ -2,6 +2,10 @@
set -e -o pipefail
if [ -d /usr/local/go ]; then
export PATH="$PATH:/usr/local/go/bin"
fi
DIR=$(dirname "$0")
PROJECT=$DIR/../..

View File

@@ -262,7 +262,7 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
return nil, E.New("auto_detect_interface unsupported on current platform")
}
interfaceMonitor.RegisterCallback(func() error {
router.logger.Info("updated default interface ", router.interfaceMonitor.DefaultInterfaceName(), ", index ", router.interfaceMonitor.DefaultInterfaceIndex())
router.logger.Info("updated default interface ", router.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", router.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()))
return nil
})
router.interfaceMonitor = interfaceMonitor
@@ -498,13 +498,13 @@ func (r *Router) DefaultOutbound(network string) adapter.Outbound {
}
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
metadata.Network = N.NetworkTCP
switch metadata.Destination.Fqdn {
case mux.Destination.Fqdn:
r.logger.InfoContext(ctx, "inbound multiplex connection")
return mux.NewConnection(ctx, r, r, r.logger, conn, metadata)
case uot.UOTMagicAddress:
r.logger.InfoContext(ctx, "inbound UoT connection")
metadata.Network = N.NetworkUDP
metadata.Destination = M.Socksaddr{}
return r.RoutePacketConnection(ctx, uot.NewClientConn(conn), metadata)
}
@@ -516,7 +516,10 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
metadata.Protocol = sniffMetadata.Protocol
metadata.Domain = sniffMetadata.Domain
if metadata.SniffOverrideDestination && sniff.IsDomainName(metadata.Domain) {
metadata.Destination.Fqdn = metadata.Domain
metadata.Destination = M.Socksaddr{
Fqdn: metadata.Domain,
Port: metadata.Destination.Port,
}
}
if metadata.Domain != "" {
r.logger.DebugContext(ctx, "sniffed protocol: ", metadata.Protocol, ", domain: ", metadata.Domain)
@@ -552,6 +555,7 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
}
func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
metadata.Network = N.NetworkUDP
if metadata.SniffEnabled {
buffer := buf.NewPacket()
buffer.FullReset()
@@ -565,7 +569,10 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
metadata.Protocol = sniffMetadata.Protocol
metadata.Domain = sniffMetadata.Domain
if metadata.SniffOverrideDestination && sniff.IsDomainName(metadata.Domain) {
metadata.Destination.Fqdn = metadata.Domain
metadata.Destination = M.Socksaddr{
Fqdn: metadata.Domain,
Port: metadata.Destination.Port,
}
}
if metadata.Domain != "" {
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain)

View File

@@ -6,51 +6,67 @@ import (
"github.com/sagernet/sing-box/adapter"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
"go4.org/netipx"
)
var _ RuleItem = (*IPCIDRItem)(nil)
type IPCIDRItem struct {
prefixes []netip.Prefix
isSource bool
ipSet *netipx.IPSet
isSource bool
description string
}
func NewIPCIDRItem(isSource bool, prefixStrings []string) (*IPCIDRItem, error) {
prefixes := make([]netip.Prefix, 0, len(prefixStrings))
var builder netipx.IPSetBuilder
for i, prefixString := range prefixStrings {
prefix, err := netip.ParsePrefix(prefixString)
if err != nil {
return nil, E.Cause(err, "parse prefix [", i, "]")
if err == nil {
builder.AddPrefix(prefix)
continue
}
prefixes = append(prefixes, prefix)
addr, addrErr := netip.ParseAddr(prefixString)
if addrErr == nil {
builder.Add(addr)
continue
}
return nil, E.Cause(err, "parse ip_cidr [", i, "]")
}
var description string
if isSource {
description = "source_ipcidr="
} else {
description = "ipcidr="
}
if dLen := len(prefixStrings); dLen == 1 {
description += prefixStrings[0]
} else if dLen > 3 {
description += "[" + strings.Join(prefixStrings[:3], " ") + "...]"
} else {
description += "[" + strings.Join(prefixStrings, " ") + "]"
}
ipSet, err := builder.IPSet()
if err != nil {
return nil, err
}
return &IPCIDRItem{
prefixes: prefixes,
isSource: isSource,
ipSet: ipSet,
isSource: isSource,
description: description,
}, nil
}
func (r *IPCIDRItem) Match(metadata *adapter.InboundContext) bool {
if r.isSource {
for _, prefix := range r.prefixes {
if prefix.Contains(metadata.Source.Addr) {
return true
}
}
return r.ipSet.Contains(metadata.Source.Addr)
} else {
if metadata.Destination.IsIP() {
for _, prefix := range r.prefixes {
if prefix.Contains(metadata.Destination.Addr) {
return true
}
}
return r.ipSet.Contains(metadata.Destination.Addr)
} else {
for _, address := range metadata.DestinationAddresses {
for _, prefix := range r.prefixes {
if prefix.Contains(address) {
return true
}
if r.ipSet.Contains(address) {
return true
}
}
}
@@ -59,17 +75,5 @@ func (r *IPCIDRItem) Match(metadata *adapter.InboundContext) bool {
}
func (r *IPCIDRItem) String() string {
var description string
if r.isSource {
description = "source_ipcidr="
} else {
description = "ipcidr="
}
pLen := len(r.prefixes)
if pLen == 1 {
description += r.prefixes[0].String()
} else {
description += "[" + strings.Join(F.MapToString(r.prefixes), " ") + "]"
}
return description
return r.description
}

View File

@@ -10,7 +10,7 @@ require (
github.com/docker/docker v20.10.17+incompatible
github.com/docker/go-connections v0.4.0
github.com/gofrs/uuid v4.2.0+incompatible
github.com/sagernet/sing v0.0.0-20220824062950-7bfd820739a8
github.com/sagernet/sing v0.0.0-20220826124916-d4ba8fdfac88
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
github.com/spyzhov/ajson v0.7.1
github.com/stretchr/testify v1.8.0
@@ -57,25 +57,25 @@ require (
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // indirect
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a // indirect
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
github.com/sagernet/netlink v0.0.0-20220820041223-3cd8365d17ac // indirect
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17 // indirect
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb // indirect
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 // indirect
github.com/sagernet/sing-tun v0.0.0-20220824105617-e5c59fc756a6 // indirect
github.com/sagernet/sing-vmess v0.0.0-20220811135656-4f3f07acf9c4 // indirect
github.com/sagernet/sing-tun v0.0.0-20220827013030-e01ce3a8a70e // indirect
github.com/sagernet/sing-vmess v0.0.0-20220827032426-01665c9c4e31 // indirect
github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.22.0 // indirect
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d // indirect
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
golang.org/x/tools v0.1.10 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478 // indirect
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect

View File

@@ -156,22 +156,22 @@ github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a h1:SE3Xn4GOQ+kx
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a/go.mod h1:Q+ZXyesnkjV5B70B1ixk65ecKrlJ2jz0atv3fPKsVVo=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
github.com/sagernet/netlink v0.0.0-20220820041223-3cd8365d17ac h1:I03d2HNy5f3INRZfsvuoLhz0h3qqsDLbKSw0EsYxQxI=
github.com/sagernet/netlink v0.0.0-20220820041223-3cd8365d17ac/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17 h1:zvm6IrIgo4rLizJCHkH+SWUBhm+jyjjozX031QdAlj8=
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb h1:wc0yQ+SBn4TaTYRwpwvEm3nc4eRdxk6vtRbouLVZAzk=
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4=
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.0.0-20220824062950-7bfd820739a8 h1:kHsinrGrMjEh5KUXC/MPCS+Uy3Z3XO/cMhC8xJtABE8=
github.com/sagernet/sing v0.0.0-20220824062950-7bfd820739a8/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
github.com/sagernet/sing v0.0.0-20220826124916-d4ba8fdfac88 h1:wxUQfVBqiUtAemytzP9mNjAkSiI0nVsRZBQvCLP8r5g=
github.com/sagernet/sing v0.0.0-20220826124916-d4ba8fdfac88/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFxUX+xJCWMPPFZCn2GC/uLrBjTSr1vHY=
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM=
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
github.com/sagernet/sing-tun v0.0.0-20220824105617-e5c59fc756a6 h1:C0uNMDrjYribl4Pu41Au9UeQROIOeMWaDd7eSUIQ9gQ=
github.com/sagernet/sing-tun v0.0.0-20220824105617-e5c59fc756a6/go.mod h1:zMKRFCEoO6Jp5Yxb2NUTqc+SvAtNVAmzfwArAheJy5g=
github.com/sagernet/sing-vmess v0.0.0-20220811135656-4f3f07acf9c4 h1:2hLETh97+S4WnfMR27XyC7QVU1SH7FTNoCznP229YJU=
github.com/sagernet/sing-vmess v0.0.0-20220811135656-4f3f07acf9c4/go.mod h1:82O6gzbxLha/W/jxSVQbsqf2lVdRTjMIgyLug0lpJps=
github.com/sagernet/sing-tun v0.0.0-20220827013030-e01ce3a8a70e h1:7GGZfIhbTAiUmVsWVLEccrKbwsgocUaJDJ859RVFNTA=
github.com/sagernet/sing-tun v0.0.0-20220827013030-e01ce3a8a70e/go.mod h1:B9BsLZmK01+9Dzhl634lM6YU80aTqOZ2yyrOzhA/Bto=
github.com/sagernet/sing-vmess v0.0.0-20220827032426-01665c9c4e31 h1:FAsJsVwpPcoITcj6/9JxRKxy8n3bIKLqKmDGVzmfeOo=
github.com/sagernet/sing-vmess v0.0.0-20220827032426-01665c9c4e31/go.mod h1:82O6gzbxLha/W/jxSVQbsqf2lVdRTjMIgyLug0lpJps=
github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939 h1:pB1Dh1NbwVrLhQhotr4O4Hs3yhiBzmg3AvnUyYjL4x4=
github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
@@ -205,6 +205,8 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
go.uber.org/zap v1.22.0 h1:Zcye5DUgBloQ9BaT4qc9BnjOFog5TvBSAGkJ3Nf70c0=
go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U=
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d h1:ggxwEf5eu0l8v+87VhX1czFh8zJul3hK16Gmruxn7hw=
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -224,8 +226,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -308,12 +310,11 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f h1:OKYpQQVE3DKSc3r3zHVzq46vq5YH7x8xpR3/k9ixmUg=
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=

View File

@@ -20,6 +20,52 @@ func TestV2RayGRPCSelf(t *testing.T) {
})
}
func TestV2RayGRPCLite(t *testing.T) {
t.Run("server", func(t *testing.T) {
testV2RayTransportSelfWith(t, &option.V2RayTransportOptions{
Type: C.V2RayTransportTypeGRPC,
GRPCOptions: option.V2RayGRPCOptions{
ServiceName: "TunService",
ForceLite: true,
},
}, &option.V2RayTransportOptions{
Type: C.V2RayTransportTypeGRPC,
GRPCOptions: option.V2RayGRPCOptions{
ServiceName: "TunService",
},
})
})
t.Run("client", func(t *testing.T) {
testV2RayTransportSelfWith(t, &option.V2RayTransportOptions{
Type: C.V2RayTransportTypeGRPC,
GRPCOptions: option.V2RayGRPCOptions{
ServiceName: "TunService",
},
}, &option.V2RayTransportOptions{
Type: C.V2RayTransportTypeGRPC,
GRPCOptions: option.V2RayGRPCOptions{
ServiceName: "TunService",
ForceLite: true,
},
})
})
t.Run("self", func(t *testing.T) {
testV2RayTransportSelfWith(t, &option.V2RayTransportOptions{
Type: C.V2RayTransportTypeGRPC,
GRPCOptions: option.V2RayGRPCOptions{
ServiceName: "TunService",
ForceLite: true,
},
}, &option.V2RayTransportOptions{
Type: C.V2RayTransportTypeGRPC,
GRPCOptions: option.V2RayGRPCOptions{
ServiceName: "TunService",
ForceLite: true,
},
})
})
}
func TestV2RayWebscoketSelf(t *testing.T) {
t.Run("basic", func(t *testing.T) {
testV2RayTransportSelf(t, &option.V2RayTransportOptions{
@@ -48,6 +94,9 @@ func TestV2RayWebscoketSelf(t *testing.T) {
func TestV2RayHTTPSelf(t *testing.T) {
testV2RayTransportSelf(t, &option.V2RayTransportOptions{
Type: C.V2RayTransportTypeHTTP,
HTTPOptions: option.V2RayHTTPOptions{
Method: "POST",
},
})
}
@@ -58,15 +107,19 @@ func TestV2RayHTTPPlainSelf(t *testing.T) {
}
func testV2RayTransportSelf(t *testing.T, transport *option.V2RayTransportOptions) {
testV2RayTransportSelfWith(t, transport, transport)
}
func testV2RayTransportSelfWith(t *testing.T, server, client *option.V2RayTransportOptions) {
t.Run("vmess", func(t *testing.T) {
testVMessTransportSelf(t, transport)
testVMessTransportSelf(t, server, client)
})
t.Run("trojan", func(t *testing.T) {
testTrojanTransportSelf(t, transport)
testTrojanTransportSelf(t, server, client)
})
}
func testVMessTransportSelf(t *testing.T, transport *option.V2RayTransportOptions) {
func testVMessTransportSelf(t *testing.T, server *option.V2RayTransportOptions, client *option.V2RayTransportOptions) {
user, err := uuid.DefaultGenerator.NewV4()
require.NoError(t, err)
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
@@ -104,7 +157,7 @@ func testVMessTransportSelf(t *testing.T, transport *option.V2RayTransportOption
CertificatePath: certPem,
KeyPath: keyPem,
},
Transport: transport,
Transport: server,
},
},
},
@@ -127,7 +180,7 @@ func testVMessTransportSelf(t *testing.T, transport *option.V2RayTransportOption
ServerName: "example.org",
CertificatePath: certPem,
},
Transport: transport,
Transport: client,
},
},
},
@@ -145,7 +198,7 @@ func testVMessTransportSelf(t *testing.T, transport *option.V2RayTransportOption
testSuit(t, clientPort, testPort)
}
func testTrojanTransportSelf(t *testing.T, transport *option.V2RayTransportOptions) {
func testTrojanTransportSelf(t *testing.T, server *option.V2RayTransportOptions, client *option.V2RayTransportOptions) {
user, err := uuid.DefaultGenerator.NewV4()
require.NoError(t, err)
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
@@ -183,7 +236,7 @@ func testTrojanTransportSelf(t *testing.T, transport *option.V2RayTransportOptio
CertificatePath: certPem,
KeyPath: keyPem,
},
Transport: transport,
Transport: server,
},
},
},
@@ -205,7 +258,7 @@ func testTrojanTransportSelf(t *testing.T, transport *option.V2RayTransportOptio
ServerName: "example.org",
CertificatePath: certPem,
},
Transport: transport,
Transport: client,
},
},
},

View File

@@ -18,7 +18,10 @@ func TestVMessAuto(t *testing.T) {
user, err := uuid.DefaultGenerator.NewV4()
require.NoError(t, err)
t.Run("self", func(t *testing.T) {
testVMessSelf(t, security, user, 0, false, false)
testVMessSelf(t, security, user, 0, false, false, false)
})
t.Run("packetaddr", func(t *testing.T) {
testVMessSelf(t, security, user, 0, false, false, true)
})
t.Run("inbound", func(t *testing.T) {
testVMessInboundWithV2Ray(t, security, user, 0, false)
@@ -49,10 +52,13 @@ func testVMess0(t *testing.T, security string) {
user, err := uuid.DefaultGenerator.NewV4()
require.NoError(t, err)
t.Run("self", func(t *testing.T) {
testVMessSelf(t, security, user, 0, false, false)
testVMessSelf(t, security, user, 0, false, false, false)
})
t.Run("self-legacy", func(t *testing.T) {
testVMessSelf(t, security, user, 1, false, false)
testVMessSelf(t, security, user, 1, false, false, false)
})
t.Run("packetaddr", func(t *testing.T) {
testVMessSelf(t, security, user, 0, false, false, true)
})
t.Run("outbound", func(t *testing.T) {
testVMessOutboundWithV2Ray(t, security, user, false, false, 0)
@@ -66,22 +72,25 @@ func testVMess1(t *testing.T, security string) {
user, err := uuid.DefaultGenerator.NewV4()
require.NoError(t, err)
t.Run("self", func(t *testing.T) {
testVMessSelf(t, security, user, 0, false, false)
testVMessSelf(t, security, user, 0, false, false, false)
})
t.Run("self-padding", func(t *testing.T) {
testVMessSelf(t, security, user, 0, true, false)
testVMessSelf(t, security, user, 0, true, false, false)
})
t.Run("self-authid", func(t *testing.T) {
testVMessSelf(t, security, user, 0, false, true)
testVMessSelf(t, security, user, 0, false, true, false)
})
t.Run("self-padding-authid", func(t *testing.T) {
testVMessSelf(t, security, user, 0, true, true)
testVMessSelf(t, security, user, 0, true, true, false)
})
t.Run("self-legacy", func(t *testing.T) {
testVMessSelf(t, security, user, 1, false, false)
testVMessSelf(t, security, user, 1, false, false, false)
})
t.Run("self-legacy-padding", func(t *testing.T) {
testVMessSelf(t, security, user, 1, true, false)
testVMessSelf(t, security, user, 1, true, false, false)
})
t.Run("packetaddr", func(t *testing.T) {
testVMessSelf(t, security, user, 0, false, false, true)
})
t.Run("inbound", func(t *testing.T) {
testVMessInboundWithV2Ray(t, security, user, 0, false)
@@ -226,10 +235,10 @@ func testVMessOutboundWithV2Ray(t *testing.T, security string, uuid uuid.UUID, g
testSuit(t, clientPort, testPort)
}
func testVMessSelf(t *testing.T, security string, uuid uuid.UUID, alterId int, globalPadding bool, authenticatedLength bool) {
func testVMessSelf(t *testing.T, security string, uuid uuid.UUID, alterId int, globalPadding bool, authenticatedLength bool, packetAddr bool) {
startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
Level: "trace",
},
Inbounds: []option.Inbound{
{
@@ -276,6 +285,7 @@ func testVMessSelf(t *testing.T, security string, uuid uuid.UUID, alterId int, g
AlterId: alterId,
GlobalPadding: globalPadding,
AuthenticatedLength: authenticatedLength,
PacketAddr: packetAddr,
},
},
},

View File

@@ -9,14 +9,22 @@ import (
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/v2raygrpc"
"github.com/sagernet/sing-box/transport/v2raygrpclite"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
func NewGRPCServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig *tls.Config, handler N.TCPConnectionHandler) (adapter.V2RayServerTransport, error) {
func NewGRPCServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig *tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
if options.ForceLite {
return v2raygrpclite.NewServer(ctx, options, tlsConfig, handler, errorHandler), nil
}
return v2raygrpc.NewServer(ctx, options, tlsConfig, handler), nil
}
func NewGRPCClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig *tls.Config) (adapter.V2RayClientTransport, error) {
if options.ForceLite {
return v2raygrpclite.NewClient(ctx, dialer, serverAddr, options, tlsConfig), nil
}
return v2raygrpc.NewClient(ctx, dialer, serverAddr, options, tlsConfig), nil
}

View File

@@ -8,17 +8,16 @@ import (
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/v2raygrpclite"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
var errGRPCNotIncluded = E.New("gRPC is not included in this build, rebuild with -tags with_grpc")
func NewGRPCServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig *tls.Config, handler N.TCPConnectionHandler) (adapter.V2RayServerTransport, error) {
return nil, errGRPCNotIncluded
func NewGRPCServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig *tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
return v2raygrpclite.NewServer(ctx, options, tlsConfig, handler, errorHandler), nil
}
func NewGRPCClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig *tls.Config) (adapter.V2RayClientTransport, error) {
return nil, errGRPCNotIncluded
return v2raygrpclite.NewClient(ctx, dialer, serverAddr, options, tlsConfig), nil
}

View File

@@ -29,7 +29,7 @@ func NewServerTransport(ctx context.Context, options option.V2RayTransportOption
}
return NewQUICServer(ctx, options.QUICOptions, tlsConfig, handler, errorHandler)
case C.V2RayTransportTypeGRPC:
return NewGRPCServer(ctx, options.GRPCOptions, tlsConfig, handler)
return NewGRPCServer(ctx, options.GRPCOptions, tlsConfig, handler, errorHandler)
default:
return nil, E.New("unknown transport type: " + options.Type)
}

View File

@@ -105,5 +105,8 @@ func wrapError(err error) error {
if strings.Contains(err.Error(), "the client connection is closing") {
return net.ErrClosed
}
if strings.Contains(err.Error(), "server closed the stream without sending trailers") {
return net.ErrClosed
}
return err
}

View File

@@ -0,0 +1,77 @@
package v2raygrpclite
import (
"context"
"crypto/tls"
"fmt"
"io"
"net"
"net/http"
"net/url"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
var _ adapter.V2RayClientTransport = (*Client)(nil)
var defaultClientHeader = http.Header{
"Content-Type": []string{"application/grpc"},
"User-Agent": []string{"grpc-go/1.48.0"},
"TE": []string{"trailers"},
}
type Client struct {
ctx context.Context
dialer N.Dialer
serverAddr M.Socksaddr
transport *http.Transport
options option.V2RayGRPCOptions
url *url.URL
}
func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig *tls.Config) adapter.V2RayClientTransport {
return &Client{
ctx: ctx,
dialer: dialer,
serverAddr: serverAddr,
options: options,
transport: &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
},
ForceAttemptHTTP2: true,
TLSClientConfig: tlsConfig,
},
url: &url.URL{
Scheme: "https",
Host: serverAddr.String(),
Path: fmt.Sprintf("/%s/Tun", url.QueryEscape(options.ServiceName)),
},
}
}
func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
pipeInReader, pipeInWriter := io.Pipe()
request := &http.Request{
Method: http.MethodPost,
Body: pipeInReader,
URL: c.url,
Proto: "HTTP/2",
ProtoMajor: 2,
Header: defaultClientHeader,
}
request = request.WithContext(ctx)
conn := newLateGunConn(pipeInWriter)
go func() {
response, err := c.transport.RoundTrip(request)
if err == nil {
conn.setup(response.Body, nil)
} else {
conn.setup(nil, err)
}
}()
return conn, nil
}

View File

@@ -0,0 +1,168 @@
package v2raygrpclite
import (
std_bufio "bufio"
"bytes"
"encoding/binary"
"io"
"net"
"net/http"
"os"
"time"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/rw"
)
// kanged from: https://github.com/Qv2ray/gun-lite
var _ net.Conn = (*GunConn)(nil)
type GunConn struct {
reader *std_bufio.Reader
writer io.Writer
flusher http.Flusher
create chan struct{}
err error
readRemaining int
}
func newGunConn(reader io.Reader, writer io.Writer, flusher http.Flusher) *GunConn {
return &GunConn{
reader: std_bufio.NewReader(reader),
writer: writer,
flusher: flusher,
}
}
func newLateGunConn(writer io.Writer) *GunConn {
return &GunConn{
create: make(chan struct{}),
writer: writer,
}
}
func (c *GunConn) setup(reader io.Reader, err error) {
c.reader = std_bufio.NewReader(reader)
c.err = err
close(c.create)
}
func (c *GunConn) Read(b []byte) (n int, err error) {
n, err = c.read(b)
return n, wrapError(err)
}
func (c *GunConn) read(b []byte) (n int, err error) {
if c.reader == nil {
<-c.create
if c.err != nil {
return 0, c.err
}
}
if c.readRemaining > 0 {
if len(b) > c.readRemaining {
b = b[:c.readRemaining]
}
n, err = c.reader.Read(b)
c.readRemaining -= n
return
}
_, err = c.reader.Discard(6)
if err != nil {
return
}
dataLen, err := binary.ReadUvarint(c.reader)
if err != nil {
return
}
readLen := int(dataLen)
c.readRemaining = readLen
if len(b) > readLen {
b = b[:readLen]
}
n, err = c.reader.Read(b)
c.readRemaining -= n
return
}
func (c *GunConn) Write(b []byte) (n int, err error) {
protobufHeader := [1 + binary.MaxVarintLen64]byte{0x0A}
varuintLen := binary.PutUvarint(protobufHeader[1:], uint64(len(b)))
grpcHeader := buf.Get(5)
grpcPayloadLen := uint32(1 + varuintLen + len(b))
binary.BigEndian.PutUint32(grpcHeader[1:5], grpcPayloadLen)
_, err = bufio.Copy(c.writer, io.MultiReader(bytes.NewReader(grpcHeader), bytes.NewReader(protobufHeader[:varuintLen+1]), bytes.NewReader(b)))
buf.Put(grpcHeader)
if f, ok := c.writer.(http.Flusher); ok {
f.Flush()
}
return len(b), wrapError(err)
}
func uLen(x uint64) int {
i := 0
for x >= 0x80 {
x >>= 7
i++
}
return i + 1
}
func (c *GunConn) WriteBuffer(buffer *buf.Buffer) error {
defer buffer.Release()
dataLen := buffer.Len()
varLen := uLen(uint64(dataLen))
header := buffer.ExtendHeader(6 + varLen)
binary.BigEndian.PutUint32(header[1:5], uint32(1+varLen+dataLen))
header[5] = 0x0A
binary.PutUvarint(header[6:], uint64(dataLen))
err := rw.WriteBytes(c.writer, buffer.Bytes())
if c.flusher != nil {
c.flusher.Flush()
}
return wrapError(err)
}
func (c *GunConn) FrontHeadroom() int {
return 6 + binary.MaxVarintLen64
}
func (c *GunConn) Close() error {
return common.Close(c.reader, c.writer)
}
func (c *GunConn) LocalAddr() net.Addr {
return nil
}
func (c *GunConn) RemoteAddr() net.Addr {
return nil
}
func (c *GunConn) SetDeadline(t time.Time) error {
return os.ErrInvalid
}
func (c *GunConn) SetReadDeadline(t time.Time) error {
return os.ErrInvalid
}
func (c *GunConn) SetWriteDeadline(t time.Time) error {
return os.ErrInvalid
}
func wrapError(err error) error {
if E.IsMulti(err, io.ErrUnexpectedEOF) {
return io.EOF
}
return err
}

View File

@@ -0,0 +1,96 @@
package v2raygrpclite
import (
"context"
"crypto/tls"
"fmt"
"net"
"net/http"
"net/url"
"os"
"strings"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
sHttp "github.com/sagernet/sing/protocol/http"
)
var _ adapter.V2RayServerTransport = (*Server)(nil)
type Server struct {
handler N.TCPConnectionHandler
errorHandler E.Handler
httpServer *http.Server
path string
}
func (s *Server) Network() []string {
return []string{N.NetworkTCP}
}
func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig *tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) *Server {
server := &Server{
handler: handler,
errorHandler: errorHandler,
path: fmt.Sprintf("/%s/Tun", url.QueryEscape(options.ServiceName)),
}
if tlsConfig != nil {
if !common.Contains(tlsConfig.NextProtos, "h2") {
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
}
}
server.httpServer = &http.Server{
Handler: server,
TLSConfig: tlsConfig,
}
return server
}
func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
if request.URL.Path != s.path {
writer.WriteHeader(http.StatusNotFound)
s.badRequest(request, E.New("bad path: ", request.URL.Path))
return
}
if request.Method != http.MethodPost {
writer.WriteHeader(http.StatusNotFound)
s.badRequest(request, E.New("bad method: ", request.Method))
return
}
if ct := request.Header.Get("Content-Type"); !strings.HasPrefix(ct, "application/grpc") {
writer.WriteHeader(http.StatusNotFound)
s.badRequest(request, E.New("bad content type: ", ct))
return
}
writer.Header().Set("Content-Type", "application/grpc")
writer.Header().Set("TE", "trailers")
writer.WriteHeader(http.StatusOK)
var metadata M.Metadata
metadata.Source = sHttp.SourceAddress(request)
conn := newGunConn(request.Body, writer, writer.(http.Flusher))
s.handler.NewConnection(request.Context(), conn, metadata)
}
func (s *Server) badRequest(request *http.Request, err error) {
s.errorHandler.NewError(request.Context(), E.Cause(err, "process connection from ", request.RemoteAddr))
}
func (s *Server) Serve(listener net.Listener) error {
if s.httpServer.TLSConfig == nil {
return s.httpServer.Serve(listener)
} else {
return s.httpServer.ServeTLS(listener, "", "")
}
}
func (s *Server) ServePacket(listener net.PacketConn) error {
return os.ErrInvalid
}
func (s *Server) Close() error {
return common.Close(common.PtrOrNil(s.httpServer))
}

View File

@@ -74,13 +74,13 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
if !c.http2 {
return c.dialHTTP()
return c.dialHTTP(ctx)
} else {
return c.dialHTTP2()
return c.dialHTTP2(ctx)
}
}
func (c *Client) dialHTTP() (net.Conn, error) {
func (c *Client) dialHTTP(ctx context.Context) (net.Conn, error) {
conn, err := c.dialer.DialContext(c.ctx, N.NetworkTCP, c.serverAddr)
if err != nil {
return nil, err
@@ -92,6 +92,7 @@ func (c *Client) dialHTTP() (net.Conn, error) {
Proto: "HTTP/1.1",
Header: c.headers.Clone(),
}
request = request.WithContext(ctx)
switch hostLen := len(c.host); hostLen {
case 0:
case 1:
@@ -114,7 +115,7 @@ func (c *Client) dialHTTP() (net.Conn, error) {
return conn, nil
}
func (c *Client) dialHTTP2() (net.Conn, error) {
func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) {
pipeInReader, pipeInWriter := io.Pipe()
request := &http.Request{
Method: c.method,
@@ -124,6 +125,7 @@ func (c *Client) dialHTTP2() (net.Conn, error) {
Proto: "HTTP/2",
Header: c.headers.Clone(),
}
request = request.WithContext(ctx)
switch hostLen := len(c.host); hostLen {
case 0:
case 1:
@@ -139,6 +141,7 @@ func (c *Client) dialHTTP2() (net.Conn, error) {
return nil, err
}
if response.StatusCode != 200 {
pipeInWriter.Close()
return nil, E.New("unexpected status: ", response.StatusCode, " ", response.Status)
}
return &HTTPConn{

View File

@@ -90,9 +90,8 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
}
writer.WriteHeader(http.StatusOK)
if f, ok := writer.(http.Flusher); ok {
f.Flush()
}
writer.(http.Flusher).Flush()
var metadata M.Metadata
metadata.Source = sHttp.SourceAddress(request)
if h, ok := writer.(http.Hijacker); ok {

View File

@@ -70,7 +70,7 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
}
return nil, wrapDialError(response, err)
} else {
return &EarlyWebsocketConn{Client: c, create: make(chan struct{})}, nil
return &EarlyWebsocketConn{Client: c, ctx: ctx, create: make(chan struct{})}, nil
}
}

View File

@@ -1,6 +1,7 @@
package v2raywebsocket
import (
"context"
"encoding/base64"
"io"
"net"
@@ -68,6 +69,7 @@ func (c *WebsocketConn) SetDeadline(t time.Time) error {
type EarlyWebsocketConn struct {
*Client
ctx context.Context
conn *WebsocketConn
create chan struct{}
}
@@ -98,14 +100,14 @@ func (c *EarlyWebsocketConn) Write(b []byte) (n int, err error) {
if len(earlyData) > 0 {
earlyDataString := base64.RawURLEncoding.EncodeToString(earlyData)
if c.earlyDataHeaderName == "" {
conn, response, err = c.dialer.Dial(c.uri+earlyDataString, c.headers)
conn, response, err = c.dialer.DialContext(c.ctx, c.uri+earlyDataString, c.headers)
} else {
headers := c.headers.Clone()
headers.Set(c.earlyDataHeaderName, earlyDataString)
conn, response, err = c.dialer.Dial(c.uri, headers)
conn, response, err = c.dialer.DialContext(c.ctx, c.uri, headers)
}
} else {
conn, response, err = c.dialer.Dial(c.uri, c.headers)
conn, response, err = c.dialer.DialContext(c.ctx, c.uri, c.headers)
}
if err != nil {
return 0, wrapDialError(response, err)