Compare commits

..

37 Commits

Author SHA1 Message Date
世界
de1b5971e1 Update documentation 2023-04-16 16:28:41 +08:00
世界
5c20d0b4d5 Update dependencies 2023-04-16 16:28:41 +08:00
世界
9df96ac7f1 Fix deadline usage on websocket conn 2023-04-16 16:28:40 +08:00
世界
87cd925144 Fix conntrack return pointer 2023-04-14 21:00:45 +08:00
世界
fecb796000 android: Remove Seq.Delete warning 2023-04-14 21:00:40 +08:00
世界
cfb6c804aa Print sniff result 2023-04-14 21:00:01 +08:00
世界
11c50c7558 Fix processing domain address in packet 2023-04-14 20:59:57 +08:00
世界
34cc7f176e Fix parsing query in http path 2023-04-14 20:59:16 +08:00
Xiaokang Wang (Shelikhoo)
b54da9c6af Fix '?' at end of WebSocket path get escaped
This fix align sing-box's behaviour with V2Ray when it comes to processing ? at the end of WebSocket's path.
2023-04-14 20:58:12 +08:00
世界
f44f86b832 Fix workflows 2023-04-14 20:57:08 +08:00
世界
4ebf40f582 Fix find process user 2023-04-14 20:56:58 +08:00
世界
53e4302143 Fix set HTTP TLS ALPN 2023-04-14 20:56:55 +08:00
世界
cf778eda4f Fix v2ray http transport server read request 2023-04-14 20:56:51 +08:00
世界
bb63429079 Update cancel context usage 2023-04-14 20:56:16 +08:00
世界
f7f9a7ae20 Fix write log to stderr 2023-04-14 20:55:57 +08:00
世界
8699412a4c platform: Add stderr redirect 2023-04-14 20:55:45 +08:00
世界
0d7aa19cd1 Fix write http status after response sent 2023-04-14 20:55:20 +08:00
世界
50a7295360 Replace usages of uber/atomic 2023-04-14 20:55:05 +08:00
世界
e57b6ae98d Update dependencies 2023-04-14 20:54:56 +08:00
世界
6843970536 Add loopback check 2023-04-08 09:13:50 +08:00
世界
62425ad3e4 Add close monitor 2023-04-08 08:10:03 +08:00
世界
e1e217854e Add start and close track message 2023-04-08 08:09:28 +08:00
世界
5bf177b021 platform: Fix build on windows 2023-04-07 21:10:16 +08:00
世界
72dbf2e2b4 documentation: Update changelog 2023-04-07 19:18:26 +08:00
世界
46c318c6fe Fix v2ray HTTP/1.1 transport compatibility 2023-04-07 18:20:07 +08:00
世界
05bb1b88c3 dns: Fix rewrite TTL 2023-04-07 16:19:34 +08:00
世界
5176ea9fe0 Update dependencies 2023-04-07 16:19:34 +08:00
世界
36d349acd2 dns: Fix calculate TTL 2023-04-07 13:12:16 +08:00
世界
4feee983b5 Update reality protocol 2023-04-06 19:05:05 +08:00
世界
9b12e3e389 Update client documentation 2023-04-06 12:51:26 +08:00
世界
afd3464216 Minor fixes 2023-04-05 21:41:06 +08:00
世界
8b64446274 platform: Fixes and improvements 2023-04-05 19:54:20 +08:00
世界
28aa4c4d1f Refactor log factory constructor 2023-04-03 20:24:13 +08:00
世界
0be3cdc8fb platform: Add http client 2023-04-03 15:12:44 +08:00
armv9
f8be484019 conntrack: Fix missing tracking for udp conn 2023-04-02 12:06:03 +08:00
世界
35f03f092d Improve UDP domain destination NAT 2023-04-02 12:05:59 +08:00
世界
c3d7401ead platform: Add check config func 2023-04-02 10:35:03 +08:00
94 changed files with 1189 additions and 574 deletions

View File

@@ -31,12 +31,6 @@ jobs:
uses: actions/setup-go@v4 uses: actions/setup-go@v4
with: with:
go-version: ${{ steps.version.outputs.go_version }} go-version: ${{ steps.version.outputs.go_version }}
- name: Cache go module
uses: actions/cache@v3
with:
path: |
~/go/pkg/mod
key: go-${{ hashFiles('**/go.sum') }}
- name: Add cache to Go proxy - name: Add cache to Go proxy
run: | run: |
version=`git rev-parse HEAD` version=`git rev-parse HEAD`
@@ -196,12 +190,6 @@ jobs:
uses: actions/setup-go@v4 uses: actions/setup-go@v4
with: with:
go-version: ${{ steps.version.outputs.go_version }} go-version: ${{ steps.version.outputs.go_version }}
- name: Cache go module
uses: actions/cache@v3
with:
path: |
~/go/pkg/mod
key: go-${{ hashFiles('**/go.sum') }}
- name: Build - name: Build
id: build id: build
run: make run: make

View File

@@ -31,12 +31,6 @@ jobs:
uses: actions/setup-go@v4 uses: actions/setup-go@v4
with: with:
go-version: ${{ steps.version.outputs.go_version }} go-version: ${{ steps.version.outputs.go_version }}
- name: Cache go module
uses: actions/cache@v3
with:
path: |
~/go/pkg/mod
key: go-${{ hashFiles('**/go.sum') }}
- name: golangci-lint - name: golangci-lint
uses: golangci/golangci-lint-action@v3 uses: golangci/golangci-lint-action@v3
with: with:

View File

@@ -77,13 +77,20 @@ test_stdio:
go mod tidy && \ go mod tidy && \
go test -v -tags "$(TAGS_TEST),force_stdio" . go test -v -tags "$(TAGS_TEST),force_stdio" .
android:
go run ./cmd/internal/build_libbox -target android
ios:
go run ./cmd/internal/build_libbox -target ios
lib: lib:
go run ./cmd/internal/build_libbox go run ./cmd/internal/build_libbox -target android
go run ./cmd/internal/build_libbox -target ios
lib_install: lib_install:
go get -v -d go get -v -d
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.0.0-20221130124640-349ebaa752ca go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.0.0-20230413023804-244d7ff07035
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.0.0-20221130124640-349ebaa752ca go install -v github.com/sagernet/gomobile/cmd/gobind@v0.0.0-20230413023804-244d7ff07035
clean: clean:
rm -rf bin dist sing-box rm -rf bin dist sing-box

View File

@@ -8,6 +8,10 @@ The universal proxy platform.
https://sing-box.sagernet.org https://sing-box.sagernet.org
## Support
https://community.sagernet.org/c/sing-box/
## License ## License
``` ```

136
box.go
View File

@@ -9,7 +9,6 @@ import (
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/experimental" "github.com/sagernet/sing-box/experimental"
"github.com/sagernet/sing-box/experimental/libbox/platform" "github.com/sagernet/sing-box/experimental/libbox/platform"
"github.com/sagernet/sing-box/inbound" "github.com/sagernet/sing-box/inbound"
@@ -31,18 +30,25 @@ type Box struct {
outbounds []adapter.Outbound outbounds []adapter.Outbound
logFactory log.Factory logFactory log.Factory
logger log.ContextLogger logger log.ContextLogger
logFile *os.File
preServices map[string]adapter.Service preServices map[string]adapter.Service
postServices map[string]adapter.Service postServices map[string]adapter.Service
done chan struct{} done chan struct{}
} }
func New(ctx context.Context, options option.Options, platformInterface platform.Interface) (*Box, error) { type Options struct {
createdAt := time.Now() option.Options
Context context.Context
PlatformInterface platform.Interface
}
func New(options Options) (*Box, error) {
ctx := options.Context
if ctx == nil {
ctx = context.Background()
}
createdAt := time.Now()
experimentalOptions := common.PtrValueOrDefault(options.Experimental) experimentalOptions := common.PtrValueOrDefault(options.Experimental)
applyDebugOptions(common.PtrValueOrDefault(experimentalOptions.Debug)) applyDebugOptions(common.PtrValueOrDefault(experimentalOptions.Debug))
var needClashAPI bool var needClashAPI bool
var needV2RayAPI bool var needV2RayAPI bool
if experimentalOptions.ClashAPI != nil && experimentalOptions.ClashAPI.ExternalController != "" { if experimentalOptions.ClashAPI != nil && experimentalOptions.ClashAPI.ExternalController != "" {
@@ -51,60 +57,20 @@ func New(ctx context.Context, options option.Options, platformInterface platform
if experimentalOptions.V2RayAPI != nil && experimentalOptions.V2RayAPI.Listen != "" { if experimentalOptions.V2RayAPI != nil && experimentalOptions.V2RayAPI.Listen != "" {
needV2RayAPI = true needV2RayAPI = true
} }
var defaultLogWriter io.Writer
logOptions := common.PtrValueOrDefault(options.Log) if options.PlatformInterface != nil {
defaultLogWriter = io.Discard
var logFactory log.Factory }
var observableLogFactory log.ObservableFactory logFactory, err := log.New(log.Options{
var logFile *os.File Options: common.PtrValueOrDefault(options.Log),
var logWriter io.Writer Observable: needClashAPI,
if logOptions.Disabled { DefaultWriter: defaultLogWriter,
observableLogFactory = log.NewNOPFactory() BaseTime: createdAt,
logFactory = observableLogFactory PlatformWriter: options.PlatformInterface,
} else { })
switch logOptions.Output { if err != nil {
case "": return nil, E.Cause(err, "create log factory")
if platformInterface != nil {
logWriter = io.Discard
} else {
logWriter = os.Stdout
}
case "stderr":
logWriter = os.Stderr
case "stdout":
logWriter = os.Stdout
default:
var err error
logFile, err = os.OpenFile(C.BasePath(logOptions.Output), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
return nil, err
}
logWriter = logFile
}
logFormatter := log.Formatter{
BaseTime: createdAt,
DisableColors: logOptions.DisableColor || logFile != nil,
DisableTimestamp: !logOptions.Timestamp && logFile != nil,
FullTimestamp: logOptions.Timestamp,
TimestampFormat: "-0700 2006-01-02 15:04:05",
}
if needClashAPI {
observableLogFactory = log.NewObservableFactory(logFormatter, logWriter, platformInterface)
logFactory = observableLogFactory
} else {
logFactory = log.NewFactory(logFormatter, logWriter, platformInterface)
}
if logOptions.Level != "" {
logLevel, err := log.ParseLevel(logOptions.Level)
if err != nil {
return nil, E.Cause(err, "parse log level")
}
logFactory.SetLevel(logLevel)
} else {
logFactory.SetLevel(log.LevelTrace)
}
} }
router, err := route.NewRouter( router, err := route.NewRouter(
ctx, ctx,
logFactory, logFactory,
@@ -112,7 +78,7 @@ func New(ctx context.Context, options option.Options, platformInterface platform
common.PtrValueOrDefault(options.DNS), common.PtrValueOrDefault(options.DNS),
common.PtrValueOrDefault(options.NTP), common.PtrValueOrDefault(options.NTP),
options.Inbounds, options.Inbounds,
platformInterface, options.PlatformInterface,
) )
if err != nil { if err != nil {
return nil, E.Cause(err, "parse route options") return nil, E.Cause(err, "parse route options")
@@ -132,7 +98,7 @@ func New(ctx context.Context, options option.Options, platformInterface platform
router, router,
logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")), logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
inboundOptions, inboundOptions,
platformInterface, options.PlatformInterface,
) )
if err != nil { if err != nil {
return nil, E.Cause(err, "parse inbound[", i, "]") return nil, E.Cause(err, "parse inbound[", i, "]")
@@ -151,6 +117,7 @@ func New(ctx context.Context, options option.Options, platformInterface platform
ctx, ctx,
router, router,
logFactory.NewLogger(F.ToString("outbound/", outboundOptions.Type, "[", tag, "]")), logFactory.NewLogger(F.ToString("outbound/", outboundOptions.Type, "[", tag, "]")),
tag,
outboundOptions) outboundOptions)
if err != nil { if err != nil {
return nil, E.Cause(err, "parse outbound[", i, "]") return nil, E.Cause(err, "parse outbound[", i, "]")
@@ -158,7 +125,7 @@ func New(ctx context.Context, options option.Options, platformInterface platform
outbounds = append(outbounds, out) outbounds = append(outbounds, out)
} }
err = router.Initialize(inbounds, outbounds, func() adapter.Outbound { err = router.Initialize(inbounds, outbounds, func() adapter.Outbound {
out, oErr := outbound.New(ctx, router, logFactory.NewLogger("outbound/direct"), option.Outbound{Type: "direct", Tag: "default"}) out, oErr := outbound.New(ctx, router, logFactory.NewLogger("outbound/direct"), "direct", option.Outbound{Type: "direct", Tag: "default"})
common.Must(oErr) common.Must(oErr)
outbounds = append(outbounds, out) outbounds = append(outbounds, out)
return out return out
@@ -169,7 +136,7 @@ func New(ctx context.Context, options option.Options, platformInterface platform
preServices := make(map[string]adapter.Service) preServices := make(map[string]adapter.Service)
postServices := make(map[string]adapter.Service) postServices := make(map[string]adapter.Service)
if needClashAPI { if needClashAPI {
clashServer, err := experimental.NewClashServer(router, observableLogFactory, common.PtrValueOrDefault(options.Experimental.ClashAPI)) clashServer, err := experimental.NewClashServer(router, logFactory.(log.ObservableFactory), common.PtrValueOrDefault(options.Experimental.ClashAPI))
if err != nil { if err != nil {
return nil, E.Cause(err, "create clash api server") return nil, E.Cause(err, "create clash api server")
} }
@@ -191,7 +158,6 @@ func New(ctx context.Context, options option.Options, platformInterface platform
createdAt: createdAt, createdAt: createdAt,
logFactory: logFactory, logFactory: logFactory,
logger: logFactory.Logger(), logger: logFactory.Logger(),
logFile: logFile,
preServices: preServices, preServices: preServices,
postServices: postServices, postServices: postServices,
done: make(chan struct{}), done: make(chan struct{}),
@@ -238,21 +204,23 @@ func (s *Box) Start() error {
func (s *Box) preStart() error { func (s *Box) preStart() error {
for serviceName, service := range s.preServices { for serviceName, service := range s.preServices {
s.logger.Trace("pre-start ", serviceName)
err := adapter.PreStart(service) err := adapter.PreStart(service)
if err != nil { if err != nil {
return E.Cause(err, "pre-start ", serviceName) return E.Cause(err, "pre-starting ", serviceName)
} }
} }
for i, out := range s.outbounds { for i, out := range s.outbounds {
var tag string
if out.Tag() == "" {
tag = F.ToString(i)
} else {
tag = out.Tag()
}
if starter, isStarter := out.(common.Starter); isStarter { if starter, isStarter := out.(common.Starter); isStarter {
s.logger.Trace("initializing outbound/", out.Type(), "[", tag, "]")
err := starter.Start() err := starter.Start()
if err != nil { if err != nil {
var tag string
if out.Tag() == "" {
tag = F.ToString(i)
} else {
tag = out.Tag()
}
return E.Cause(err, "initialize outbound/", out.Type(), "[", tag, "]") return E.Cause(err, "initialize outbound/", out.Type(), "[", tag, "]")
} }
} }
@@ -266,24 +234,27 @@ func (s *Box) start() error {
return err return err
} }
for serviceName, service := range s.preServices { for serviceName, service := range s.preServices {
s.logger.Trace("starting ", serviceName)
err = service.Start() err = service.Start()
if err != nil { if err != nil {
return E.Cause(err, "start ", serviceName) return E.Cause(err, "start ", serviceName)
} }
} }
for i, in := range s.inbounds { for i, in := range s.inbounds {
var tag string
if in.Tag() == "" {
tag = F.ToString(i)
} else {
tag = in.Tag()
}
s.logger.Trace("initializing inbound/", in.Type(), "[", tag, "]")
err = in.Start() err = in.Start()
if err != nil { if err != nil {
var tag string
if in.Tag() == "" {
tag = F.ToString(i)
} else {
tag = in.Tag()
}
return E.Cause(err, "initialize inbound/", in.Type(), "[", tag, "]") return E.Cause(err, "initialize inbound/", in.Type(), "[", tag, "]")
} }
} }
for serviceName, service := range s.postServices { for serviceName, service := range s.postServices {
s.logger.Trace("starting ", service)
err = service.Start() err = service.Start()
if err != nil { if err != nil {
return E.Cause(err, "start ", serviceName) return E.Cause(err, "start ", serviceName)
@@ -301,40 +272,41 @@ func (s *Box) Close() error {
} }
var errors error var errors error
for serviceName, service := range s.postServices { for serviceName, service := range s.postServices {
s.logger.Trace("closing ", serviceName)
errors = E.Append(errors, service.Close(), func(err error) error { errors = E.Append(errors, service.Close(), func(err error) error {
return E.Cause(err, "close ", serviceName) return E.Cause(err, "close ", serviceName)
}) })
} }
for i, in := range s.inbounds { for i, in := range s.inbounds {
s.logger.Trace("closing inbound/", in.Type(), "[", i, "]")
errors = E.Append(errors, in.Close(), func(err error) error { errors = E.Append(errors, in.Close(), func(err error) error {
return E.Cause(err, "close inbound/", in.Type(), "[", i, "]") return E.Cause(err, "close inbound/", in.Type(), "[", i, "]")
}) })
} }
for i, out := range s.outbounds { for i, out := range s.outbounds {
s.logger.Trace("closing outbound/", out.Type(), "[", i, "]")
errors = E.Append(errors, common.Close(out), func(err error) error { errors = E.Append(errors, common.Close(out), func(err error) error {
return E.Cause(err, "close inbound/", out.Type(), "[", i, "]") return E.Cause(err, "close outbound/", out.Type(), "[", i, "]")
}) })
} }
s.logger.Trace("closing router")
if err := common.Close(s.router); err != nil { if err := common.Close(s.router); err != nil {
errors = E.Append(errors, err, func(err error) error { errors = E.Append(errors, err, func(err error) error {
return E.Cause(err, "close router") return E.Cause(err, "close router")
}) })
} }
for serviceName, service := range s.preServices { for serviceName, service := range s.preServices {
s.logger.Trace("closing ", serviceName)
errors = E.Append(errors, service.Close(), func(err error) error { errors = E.Append(errors, service.Close(), func(err error) error {
return E.Cause(err, "close ", serviceName) return E.Cause(err, "close ", serviceName)
}) })
} }
s.logger.Trace("closing log factory")
if err := common.Close(s.logFactory); err != nil { if err := common.Close(s.logFactory); err != nil {
errors = E.Append(errors, err, func(err error) error { errors = E.Append(errors, err, func(err error) error {
return E.Cause(err, "close log factory") return E.Cause(err, "close log factory")
}) })
} }
if s.logFile != nil {
errors = E.Append(errors, s.logFile.Close(), func(err error) error {
return E.Cause(err, "close log file")
})
}
return errors return errors
} }

View File

@@ -31,7 +31,10 @@ func check() error {
return err return err
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
instance, err := box.New(ctx, options, nil) instance, err := box.New(box.Options{
Context: ctx,
Options: options,
})
if err == nil { if err == nil {
instance.Close() instance.Close()
} }

View File

@@ -10,6 +10,7 @@ import (
"sort" "sort"
"strings" "strings"
"syscall" "syscall"
"time"
"github.com/sagernet/sing-box" "github.com/sagernet/sing-box"
"github.com/sagernet/sing-box/common/badjsonmerge" "github.com/sagernet/sing-box/common/badjsonmerge"
@@ -127,7 +128,10 @@ func create() (*box.Box, context.CancelFunc, error) {
options.Log.DisableColor = true options.Log.DisableColor = true
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
instance, err := box.New(ctx, options, nil) instance, err := box.New(box.Options{
Context: ctx,
Options: options,
})
if err != nil { if err != nil {
cancel() cancel()
return nil, nil, E.Cause(err, "create service") return nil, nil, E.Cause(err, "create service")
@@ -174,7 +178,10 @@ func run() error {
} }
} }
cancel() cancel()
closeCtx, closed := context.WithCancel(context.Background())
go closeMonitor(closeCtx)
instance.Close() instance.Close()
closed()
if osSignal != syscall.SIGHUP { if osSignal != syscall.SIGHUP {
return nil return nil
} }
@@ -182,3 +189,13 @@ func run() error {
} }
} }
} }
func closeMonitor(ctx context.Context) {
time.Sleep(3 * time.Second)
select {
case <-ctx.Done():
return
default:
}
log.Fatal("sing-box did not close!")
}

View File

@@ -1,8 +1,6 @@
package main package main
import ( import (
"context"
"github.com/sagernet/sing-box" "github.com/sagernet/sing-box"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
@@ -27,7 +25,7 @@ func createPreStartedClient() (*box.Box, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
instance, err := box.New(context.Background(), options, nil) instance, err := box.New(box.Options{Options: options})
if err != nil { if err != nil {
return nil, E.Cause(err, "create service") return nil, E.Cause(err, "create service")
} }

View File

@@ -12,7 +12,7 @@ type Conn struct {
element *list.Element[io.Closer] element *list.Element[io.Closer]
} }
func NewConn(conn net.Conn) (*Conn, error) { func NewConn(conn net.Conn) (net.Conn, error) {
connAccess.Lock() connAccess.Lock()
element := openConnection.PushBack(conn) element := openConnection.PushBack(conn)
connAccess.Unlock() connAccess.Unlock()

View File

@@ -12,7 +12,7 @@ type PacketConn struct {
element *list.Element[io.Closer] element *list.Element[io.Closer]
} }
func NewPacketConn(conn net.PacketConn) (*PacketConn, error) { func NewPacketConn(conn net.PacketConn) (net.PacketConn, error) {
connAccess.Lock() connAccess.Lock()
element := openConnection.PushBack(conn) element := openConnection.PushBack(conn)
connAccess.Unlock() connAccess.Unlock()

View File

@@ -14,10 +14,16 @@ var (
) )
func Count() int { func Count() int {
if !Enabled {
return 0
}
return openConnection.Len() return openConnection.Len()
} }
func List() []io.Closer { func List() []io.Closer {
if !Enabled {
return nil
}
connAccess.RLock() connAccess.RLock()
defer connAccess.RUnlock() defer connAccess.RUnlock()
connList := make([]io.Closer, 0, openConnection.Len()) connList := make([]io.Closer, 0, openConnection.Len())
@@ -28,6 +34,9 @@ func List() []io.Closer {
} }
func Close() { func Close() {
if !Enabled {
return
}
connAccess.Lock() connAccess.Lock()
defer connAccess.Unlock() defer connAccess.Unlock()
for element := openConnection.Front(); element != nil; element = element.Next() { for element := openConnection.Front(); element != nil; element = element.Next() {

View File

@@ -154,9 +154,9 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
switch N.NetworkName(network) { switch N.NetworkName(network) {
case N.NetworkUDP: case N.NetworkUDP:
if !address.IsIPv6() { if !address.IsIPv6() {
return d.udpDialer4.DialContext(ctx, network, address.String()) return trackConn(d.udpDialer4.DialContext(ctx, network, address.String()))
} else { } else {
return d.udpDialer6.DialContext(ctx, network, address.String()) return trackConn(d.udpDialer6.DialContext(ctx, network, address.String()))
} }
} }
if !address.IsIPv6() { if !address.IsIPv6() {

View File

@@ -9,6 +9,7 @@ import (
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-dns" "github.com/sagernet/sing-dns"
"github.com/sagernet/sing/common/bufio"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
) )
@@ -68,11 +69,11 @@ func (d *ResolveDialer) ListenPacket(ctx context.Context, destination M.Socksadd
if err != nil { if err != nil {
return nil, err return nil, err
} }
conn, err := N.ListenSerial(ctx, d.dialer, destination, addresses) conn, destinationAddress, err := N.ListenSerial(ctx, d.dialer, destination, addresses)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return NewResolvePacketConn(ctx, d.router, d.strategy, conn), nil return bufio.NewNATPacketConn(bufio.NewPacketConn(conn), M.SocksaddrFrom(destinationAddress, destination.Port), destination), nil
} }
func (d *ResolveDialer) Upstream() any { func (d *ResolveDialer) Upstream() any {

View File

@@ -1,84 +0,0 @@
package dialer
import (
"context"
"net"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-dns"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
func NewResolvePacketConn(ctx context.Context, router adapter.Router, strategy dns.DomainStrategy, conn net.PacketConn) N.NetPacketConn {
if udpConn, ok := conn.(*net.UDPConn); ok {
return &ResolveUDPConn{udpConn, ctx, router, strategy}
} else {
return &ResolvePacketConn{conn, ctx, router, strategy}
}
}
type ResolveUDPConn struct {
*net.UDPConn
ctx context.Context
router adapter.Router
strategy dns.DomainStrategy
}
func (w *ResolveUDPConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {
n, addr, err := w.ReadFromUDPAddrPort(buffer.FreeBytes())
if err != nil {
return M.Socksaddr{}, err
}
buffer.Truncate(n)
return M.SocksaddrFromNetIP(addr), nil
}
func (w *ResolveUDPConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
defer buffer.Release()
if destination.IsFqdn() {
addresses, err := w.router.Lookup(w.ctx, destination.Fqdn, w.strategy)
if err != nil {
return err
}
return common.Error(w.UDPConn.WriteToUDPAddrPort(buffer.Bytes(), M.SocksaddrFrom(addresses[0], destination.Port).AddrPort()))
}
return common.Error(w.UDPConn.WriteToUDPAddrPort(buffer.Bytes(), destination.AddrPort()))
}
func (w *ResolveUDPConn) Upstream() any {
return w.UDPConn
}
type ResolvePacketConn struct {
net.PacketConn
ctx context.Context
router adapter.Router
strategy dns.DomainStrategy
}
func (w *ResolvePacketConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {
_, addr, err := buffer.ReadPacketFrom(w)
if err != nil {
return M.Socksaddr{}, err
}
return M.SocksaddrFromNet(addr), err
}
func (w *ResolvePacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
defer buffer.Release()
if destination.IsFqdn() {
addresses, err := w.router.Lookup(w.ctx, destination.Fqdn, w.strategy)
if err != nil {
return err
}
return common.Error(w.WriteTo(buffer.Bytes(), M.SocksaddrFrom(addresses[0], destination.Port).UDPAddr()))
}
return common.Error(w.WriteTo(buffer.Bytes(), destination.UDPAddr()))
}
func (w *ResolvePacketConn) Upstream() any {
return w.PacketConn
}

View File

@@ -413,7 +413,11 @@ func (c *ClientPacketAddrConn) ReadFrom(p []byte) (n int, addr net.Addr, err err
if err != nil { if err != nil {
return return
} }
addr = destination.UDPAddr() if destination.IsFqdn() {
addr = destination
} else {
addr = destination.UDPAddr()
}
var length uint16 var length uint16
err = binary.Read(c.ExtendedConn, binary.BigEndian, &length) err = binary.Read(c.ExtendedConn, binary.BigEndian, &length)
if err != nil { if err != nil {

View File

@@ -3,10 +3,12 @@ package process
import ( import (
"context" "context"
"net/netip" "net/netip"
"os/user"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-tun" "github.com/sagernet/sing-tun"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
) )
type Searcher interface { type Searcher interface {
@@ -28,5 +30,15 @@ type Info struct {
} }
func FindProcessInfo(searcher Searcher, ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) { func FindProcessInfo(searcher Searcher, ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
return findProcessInfo(searcher, ctx, network, source, destination) info, err := searcher.FindProcessInfo(ctx, network, source, destination)
if err != nil {
return nil, err
}
if info.UserId != -1 {
osUser, _ := user.LookupId(F.ToString(info.UserId))
if osUser != nil {
info.User = osUser.Username
}
}
return info, nil
} }

View File

@@ -1,25 +0,0 @@
//go:build linux && !android
package process
import (
"context"
"net/netip"
"os/user"
F "github.com/sagernet/sing/common/format"
)
func findProcessInfo(searcher Searcher, ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
info, err := searcher.FindProcessInfo(ctx, network, source, destination)
if err != nil {
return nil, err
}
if info.UserId != -1 {
osUser, _ := user.LookupId(F.ToString(info.UserId))
if osUser != nil {
info.User = osUser.Username
}
}
return info, nil
}

View File

@@ -1,12 +0,0 @@
//go:build !linux || android
package process
import (
"context"
"net/netip"
)
func findProcessInfo(searcher Searcher, ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
return searcher.FindProcessInfo(ctx, network, source, destination)
}

View File

@@ -24,12 +24,12 @@ func PeekStream(ctx context.Context, conn net.Conn, buffer *buf.Buffer, timeout
} }
err := conn.SetReadDeadline(time.Now().Add(timeout)) err := conn.SetReadDeadline(time.Now().Add(timeout))
if err != nil { if err != nil {
return nil, err return nil, E.Cause(err, "set read deadline")
} }
_, err = buffer.ReadOnceFrom(conn) _, err = buffer.ReadOnceFrom(conn)
err = E.Errors(err, conn.SetReadDeadline(time.Time{})) err = E.Errors(err, conn.SetReadDeadline(time.Time{}))
if err != nil { if err != nil {
return nil, err return nil, E.Cause(err, "read payload")
} }
var metadata *adapter.InboundContext var metadata *adapter.InboundContext
var errors []error var errors []error

View File

@@ -124,8 +124,9 @@ func (e *RealityClientConfig) ClientHandshake(ctx context.Context, conn net.Conn
binary.BigEndian.PutUint64(hello.SessionId, uint64(nowTime.Unix())) binary.BigEndian.PutUint64(hello.SessionId, uint64(nowTime.Unix()))
hello.SessionId[0] = 1 hello.SessionId[0] = 1
hello.SessionId[1] = 7 hello.SessionId[1] = 8
hello.SessionId[2] = 5 hello.SessionId[2] = 0
binary.BigEndian.PutUint32(hello.SessionId[4:], uint32(time.Now().Unix()))
copy(hello.SessionId[8:], e.shortID[:]) copy(hello.SessionId[8:], e.shortID[:])
if debug.Enabled { if debug.Enabled {

View File

@@ -180,7 +180,7 @@ func NewSTDServer(ctx context.Context, router adapter.Router, logger log.Logger,
tlsConfig.ServerName = options.ServerName tlsConfig.ServerName = options.ServerName
} }
if len(options.ALPN) > 0 { if len(options.ALPN) > 0 {
tlsConfig.NextProtos = append(tlsConfig.NextProtos, options.ALPN...) tlsConfig.NextProtos = append(options.ALPN, tlsConfig.NextProtos...)
} }
if options.MinVersion != "" { if options.MinVersion != "" {
minVersion, err := ParseTLSVersion(options.MinVersion) minVersion, err := ParseTLSVersion(options.MinVersion)

View File

@@ -1,3 +1,16 @@
#### 1.2.4
* Fixes and improvements
#### 1.2.3
* Introducing our [new Android client application](/installation/clients/sfa)
* Improve UDP domain destination NAT
* Update reality protocol
* Fix TTL calculation for DNS response
* Fix v2ray HTTP transport compatibility
* Fix bugs and update dependencies
#### 1.2.2 #### 1.2.2
* Accept `any` outbound in dns rule **1** * Accept `any` outbound in dns rule **1**
@@ -5,7 +18,8 @@
*1*: *1*:
Now you can use the `any` outbound rule to match server address queries instead of filling in all server domains to `domain` rule. Now you can use the `any` outbound rule to match server address queries instead of filling in all server domains
to `domain` rule.
#### 1.2.1 #### 1.2.1

View File

@@ -0,0 +1,16 @@
# SFA
Experimental Android client for sing-box.
#### Requirements
* Android 5.0+
#### Download
* [AppCenter](https://install.appcenter.ms/users/nekohasekai/apps/sfa/distribution_groups/publictest)
#### Note
* Working directory is at `/sdcard/Android/data/io.nekohasekai.sfa/files` (External files directory)
* User Agent is `SFA/$version ($version_code; sing-box $sing_box_version)` in the remote profile request

View File

@@ -0,0 +1,16 @@
# SFA
实验性的 Android sing-box 客户端。
#### 要求
* Android 5.0+
#### 下载
* [AppCenter](https://install.appcenter.ms/users/nekohasekai/apps/sfa/distribution_groups/publictest)
#### 注意事项
* 工作目录位于 `/sdcard/Android/data/io.nekohasekai.sfa/files` (外部文件目录)
* 远程配置文件请求中的 User Agent 为 `SFA/$version ($version_code; sing-box $sing_box_version)`

View File

@@ -1,6 +1,6 @@
# SFI # SFI
Experimental official iOS client for sing-box. Experimental iOS client for sing-box.
#### Requirements #### Requirements
@@ -11,9 +11,10 @@ Experimental official iOS client for sing-box.
* [TestFlight](https://testflight.apple.com/join/c6ylui2j) * [TestFlight](https://testflight.apple.com/join/c6ylui2j)
#### Limit #### Note
* `system` tun stack not working * `system` tun stack not working on iOS
* User Agent is `SFI/$version ($version_code; sing-box $sing_box_version)` in the remote profile request
#### Privacy policy #### Privacy policy

View File

@@ -1,6 +1,6 @@
# SFI # SFI
实验性的官方 iOS sing-box 客户端。 实验性的 iOS sing-box 客户端。
#### 要求 #### 要求
@@ -11,9 +11,10 @@
* [TestFlight](https://testflight.apple.com/join/c6ylui2j) * [TestFlight](https://testflight.apple.com/join/c6ylui2j)
#### 限制 #### 注意事项
* `system` tun stack 不工作 * `system` tun stack 在 iOS 不工作
* 远程配置文件请求中的 User Agent 为 `SFI/$version ($version_code; sing-box $sing_box_version)`
#### 隐私政策 #### 隐私政策

View File

@@ -4,32 +4,26 @@ import (
"time" "time"
"github.com/sagernet/sing-box/experimental/clashapi/compatible" "github.com/sagernet/sing-box/experimental/clashapi/compatible"
"github.com/sagernet/sing/common/atomic"
"go.uber.org/atomic"
) )
type Manager struct { type Manager struct {
connections compatible.Map[string, tracker] uploadTemp atomic.Int64
uploadTemp *atomic.Int64 downloadTemp atomic.Int64
downloadTemp *atomic.Int64 uploadBlip atomic.Int64
uploadBlip *atomic.Int64 downloadBlip atomic.Int64
downloadBlip *atomic.Int64 uploadTotal atomic.Int64
uploadTotal *atomic.Int64 downloadTotal atomic.Int64
downloadTotal *atomic.Int64
ticker *time.Ticker connections compatible.Map[string, tracker]
done chan struct{} ticker *time.Ticker
done chan struct{}
} }
func NewManager() *Manager { func NewManager() *Manager {
manager := &Manager{ manager := &Manager{
uploadTemp: atomic.NewInt64(0), ticker: time.NewTicker(time.Second),
downloadTemp: atomic.NewInt64(0), done: make(chan struct{}),
uploadBlip: atomic.NewInt64(0),
downloadBlip: atomic.NewInt64(0),
uploadTotal: atomic.NewInt64(0),
downloadTotal: atomic.NewInt64(0),
ticker: time.NewTicker(time.Second),
done: make(chan struct{}),
} }
go manager.handle() go manager.handle()
return manager return manager

View File

@@ -1,6 +1,7 @@
package trafficontrol package trafficontrol
import ( import (
"encoding/json"
"net" "net"
"net/netip" "net/netip"
"time" "time"
@@ -8,10 +9,10 @@ import (
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/experimental/trackerconn" "github.com/sagernet/sing-box/experimental/trackerconn"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/atomic"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/gofrs/uuid" "github.com/gofrs/uuid/v5"
"go.uber.org/atomic"
) )
type Metadata struct { type Metadata struct {
@@ -43,6 +44,19 @@ type trackerInfo struct {
RulePayload string `json:"rulePayload"` RulePayload string `json:"rulePayload"`
} }
func (t trackerInfo) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]any{
"id": t.UUID.String(),
"metadata": t.Metadata,
"upload": t.UploadTotal.Load(),
"download": t.DownloadTotal.Load(),
"start": t.Start,
"chains": t.Chain,
"rule": t.Rule,
"rulePayload": t.RulePayload,
})
}
type tcpTracker struct { type tcpTracker struct {
N.ExtendedConn `json:"-"` N.ExtendedConn `json:"-"`
*trackerInfo *trackerInfo
@@ -97,8 +111,8 @@ func NewTCPTracker(conn net.Conn, manager *Manager, metadata Metadata, router ad
next = group.Now() next = group.Now()
} }
upload := atomic.NewInt64(0) upload := new(atomic.Int64)
download := atomic.NewInt64(0) download := new(atomic.Int64)
t := &tcpTracker{ t := &tcpTracker{
ExtendedConn: trackerconn.NewHook(conn, func(n int64) { ExtendedConn: trackerconn.NewHook(conn, func(n int64) {
@@ -184,8 +198,8 @@ func NewUDPTracker(conn N.PacketConn, manager *Manager, metadata Metadata, route
next = group.Now() next = group.Now()
} }
upload := atomic.NewInt64(0) upload := new(atomic.Int64)
download := atomic.NewInt64(0) download := new(atomic.Int64)
ut := &udpTracker{ ut := &udpTracker{
PacketConn: trackerconn.NewHookPacket(conn, func(n int64) { PacketConn: trackerconn.NewHookPacket(conn, func(n int64) {

View File

@@ -1,5 +1,3 @@
//go:build darwin
package libbox package libbox
const ( const (

View File

@@ -1,5 +1,3 @@
//go:build darwin
package libbox package libbox
import ( import (
@@ -46,6 +44,7 @@ func clientConnect(sharedDirectory string) (net.Conn, error) {
} }
func (c *CommandClient) Connect() error { func (c *CommandClient) Connect() error {
common.Close(c.conn)
conn, err := clientConnect(c.sharedDirectory) conn, err := clientConnect(c.sharedDirectory)
if err != nil { if err != nil {
return err return err

View File

@@ -1,5 +1,3 @@
//go:build darwin
package libbox package libbox
import ( import (

View File

@@ -1,5 +1,3 @@
//go:build darwin
package libbox package libbox
import ( import (

View File

@@ -1,5 +1,3 @@
//go:build darwin
package libbox package libbox
import ( import (

View File

@@ -1,5 +1,3 @@
//go:build darwin
package libbox package libbox
import ( import (
@@ -10,6 +8,8 @@ import (
"sync" "sync"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/debug"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/observable" "github.com/sagernet/sing/common/observable"
"github.com/sagernet/sing/common/x/list" "github.com/sagernet/sing/common/x/list"
@@ -57,7 +57,10 @@ func (s *CommandServer) Start() error {
} }
func (s *CommandServer) Close() error { func (s *CommandServer) Close() error {
return s.listener.Close() return common.Close(
s.listener,
s.observer,
)
} }
func (s *CommandServer) loopConnection(listener net.Listener) { func (s *CommandServer) loopConnection(listener net.Listener) {
@@ -69,7 +72,9 @@ func (s *CommandServer) loopConnection(listener net.Listener) {
go func() { go func() {
hErr := s.handleConnection(conn) hErr := s.handleConnection(conn)
if hErr != nil && !E.IsClosed(err) { if hErr != nil && !E.IsClosed(err) {
log.Warn("log-server: process connection: ", hErr) if debug.Enabled {
log.Warn("log-server: process connection: ", hErr)
}
} }
}() }()
} }

View File

@@ -1,5 +1,3 @@
//go:build darwin
package libbox package libbox
import ( import (
@@ -44,7 +42,7 @@ func (s *CommandServer) handleStatusConn(conn net.Conn) error {
} }
select { select {
case <-ctx.Done(): case <-ctx.Done():
return nil return ctx.Err()
case <-ticker.C: case <-ticker.C:
} }
} }

View File

@@ -1,5 +1,3 @@
//go:build darwin
package libbox package libbox
import ( import (

View File

@@ -1,8 +1,11 @@
//go:build linux || darwin
package libbox package libbox
import ( import (
"bytes"
"context"
"encoding/json"
"github.com/sagernet/sing-box"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
) )
@@ -15,3 +18,36 @@ func parseConfig(configContent string) (option.Options, error) {
} }
return options, nil return options, nil
} }
func CheckConfig(configContent string) error {
options, err := parseConfig(configContent)
if err != nil {
return err
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
instance, err := box.New(box.Options{
Context: ctx,
Options: options,
})
if err == nil {
instance.Close()
}
return err
}
func FormatConfig(configContent string) (string, error) {
options, err := parseConfig(configContent)
if err != nil {
return "", err
}
var buffer bytes.Buffer
json.NewEncoder(&buffer)
encoder := json.NewEncoder(&buffer)
encoder.SetIndent("", " ")
err = encoder.Encode(options)
if err != nil {
return "", err
}
return buffer.String(), nil
}

241
experimental/libbox/http.go Normal file
View File

@@ -0,0 +1,241 @@
package libbox
import (
"bytes"
"context"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"encoding/hex"
"errors"
"fmt"
"io"
"math/rand"
"net"
"net/http"
"net/url"
"os"
"strconv"
"sync"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/protocol/socks"
"github.com/sagernet/sing/protocol/socks/socks5"
)
type HTTPClient interface {
RestrictedTLS()
ModernTLS()
PinnedTLS12()
PinnedSHA256(sumHex string)
TrySocks5(port int32)
KeepAlive()
NewRequest() HTTPRequest
Close()
}
type HTTPRequest interface {
SetURL(link string) error
SetMethod(method string)
SetHeader(key string, value string)
SetContent(content []byte)
SetContentString(content string)
RandomUserAgent()
SetUserAgent(userAgent string)
Execute() (HTTPResponse, error)
}
type HTTPResponse interface {
GetContent() ([]byte, error)
GetContentString() (string, error)
WriteTo(path string) error
}
var (
_ HTTPClient = (*httpClient)(nil)
_ HTTPRequest = (*httpRequest)(nil)
_ HTTPResponse = (*httpResponse)(nil)
)
type httpClient struct {
tls tls.Config
client http.Client
transport http.Transport
}
func NewHTTPClient() HTTPClient {
client := new(httpClient)
client.client.Timeout = C.TCPTimeout
client.client.Transport = &client.transport
client.transport.TLSClientConfig = &client.tls
client.transport.DisableKeepAlives = true
return client
}
func (c *httpClient) ModernTLS() {
c.tls.MinVersion = tls.VersionTLS12
c.tls.CipherSuites = common.Map(tls.CipherSuites(), func(it *tls.CipherSuite) uint16 { return it.ID })
}
func (c *httpClient) RestrictedTLS() {
c.tls.MinVersion = tls.VersionTLS13
c.tls.CipherSuites = common.Map(common.Filter(tls.CipherSuites(), func(it *tls.CipherSuite) bool {
return common.Contains(it.SupportedVersions, uint16(tls.VersionTLS13))
}), func(it *tls.CipherSuite) uint16 {
return it.ID
})
}
func (c *httpClient) PinnedTLS12() {
c.tls.MinVersion = tls.VersionTLS12
c.tls.MaxVersion = tls.VersionTLS12
}
func (c *httpClient) PinnedSHA256(sumHex string) {
c.tls.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
for _, rawCert := range rawCerts {
certSum := sha256.Sum256(rawCert)
if sumHex == hex.EncodeToString(certSum[:]) {
return nil
}
}
return E.New("pinned sha256 sum mismatch")
}
}
func (c *httpClient) TrySocks5(port int32) {
dialer := new(net.Dialer)
c.transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
for {
socksConn, err := dialer.DialContext(ctx, "tcp", "127.0.0.1:"+strconv.Itoa(int(port)))
if err != nil {
break
}
_, err = socks.ClientHandshake5(socksConn, socks5.CommandConnect, M.ParseSocksaddr(addr), "", "")
if err != nil {
break
}
//nolint:staticcheck
return socksConn, err
}
return dialer.DialContext(ctx, network, addr)
}
}
func (c *httpClient) KeepAlive() {
c.transport.ForceAttemptHTTP2 = true
c.transport.DisableKeepAlives = false
}
func (c *httpClient) NewRequest() HTTPRequest {
req := &httpRequest{httpClient: c}
req.request = http.Request{
Method: "GET",
Header: http.Header{},
}
return req
}
func (c *httpClient) Close() {
c.transport.CloseIdleConnections()
}
type httpRequest struct {
*httpClient
request http.Request
}
func (r *httpRequest) SetURL(link string) (err error) {
r.request.URL, err = url.Parse(link)
if r.request.URL.User != nil {
user := r.request.URL.User.Username()
password, _ := r.request.URL.User.Password()
r.request.SetBasicAuth(user, password)
}
return
}
func (r *httpRequest) SetMethod(method string) {
r.request.Method = method
}
func (r *httpRequest) SetHeader(key string, value string) {
r.request.Header.Set(key, value)
}
func (r *httpRequest) RandomUserAgent() {
r.request.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", rand.Int()%54, rand.Int()%2))
}
func (r *httpRequest) SetUserAgent(userAgent string) {
r.request.Header.Set("User-Agent", userAgent)
}
func (r *httpRequest) SetContent(content []byte) {
buffer := bytes.Buffer{}
buffer.Write(content)
r.request.Body = io.NopCloser(bytes.NewReader(buffer.Bytes()))
r.request.ContentLength = int64(len(content))
}
func (r *httpRequest) SetContentString(content string) {
r.SetContent([]byte(content))
}
func (r *httpRequest) Execute() (HTTPResponse, error) {
response, err := r.client.Do(&r.request)
if err != nil {
return nil, err
}
httpResp := &httpResponse{Response: response}
if response.StatusCode != http.StatusOK {
return nil, errors.New(httpResp.errorString())
}
return httpResp, nil
}
type httpResponse struct {
*http.Response
getContentOnce sync.Once
content []byte
contentError error
}
func (h *httpResponse) errorString() string {
content, err := h.GetContentString()
if err != nil {
return fmt.Sprint("HTTP ", h.Status)
}
return fmt.Sprint("HTTP ", h.Status, ": ", content)
}
func (h *httpResponse) GetContent() ([]byte, error) {
h.getContentOnce.Do(func() {
defer h.Body.Close()
h.content, h.contentError = io.ReadAll(h.Body)
})
return h.content, h.contentError
}
func (h *httpResponse) GetContentString() (string, error) {
content, err := h.GetContent()
if err != nil {
return "", err
}
return string(content), nil
}
func (h *httpResponse) WriteTo(path string) error {
defer h.Body.Close()
file, err := os.Create(path)
if err != nil {
return err
}
defer file.Close()
return common.Error(bufio.Copy(file, h.Body))
}

View File

@@ -1,5 +1,3 @@
//go:build linux || darwin
package libbox package libbox
import "github.com/sagernet/sing/common" import "github.com/sagernet/sing/common"

View File

@@ -0,0 +1,29 @@
//go:build darwin || linux
package libbox
import (
"os"
"golang.org/x/sys/unix"
)
var stderrFile *os.File
func RedirectStderr(path string) error {
if stats, err := os.Stat(path); err == nil && stats.Size() > 0 {
_ = os.Rename(path, path+".old")
}
outputFile, err := os.Create(path)
if err != nil {
return err
}
err = unix.Dup2(int(outputFile.Fd()), int(os.Stderr.Fd()))
if err != nil {
outputFile.Close()
os.Remove(outputFile.Name())
return err
}
stderrFile = outputFile
return nil
}

View File

@@ -1,5 +1,3 @@
//go:build linux || darwin
package libbox package libbox
import "github.com/sagernet/sing-box/option" import "github.com/sagernet/sing-box/option"

View File

@@ -1,5 +1,3 @@
//go:build linux || darwin
package libbox package libbox
import ( import (

View File

@@ -1,5 +1,3 @@
//go:build linux || darwin
package libbox package libbox
import ( import (
@@ -30,7 +28,11 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
return nil, err return nil, err
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
instance, err := box.New(ctx, options, &platformInterfaceWrapper{platformInterface, platformInterface.UseProcFS()}) instance, err := box.New(box.Options{
Context: ctx,
Options: options,
PlatformInterface: &platformInterfaceWrapper{platformInterface, platformInterface.UseProcFS()},
})
if err != nil { if err != nil {
cancel() cancel()
return nil, E.Cause(err, "create service") return nil, E.Cause(err, "create service")
@@ -77,7 +79,7 @@ func (w *platformInterfaceWrapper) OpenTun(options tun.Options, platformOptions
if err != nil { if err != nil {
return nil, err return nil, err
} }
dupFd, err := syscall.Dup(int(tunFd)) dupFd, err := dup(int(tunFd))
if err != nil { if err != nil {
return nil, E.Cause(err, "dup tun file descriptor") return nil, E.Cause(err, "dup tun file descriptor")
} }

View File

@@ -0,0 +1,9 @@
//go:build !windows
package libbox
import "syscall"
func dup(fd int) (nfd int, err error) {
return syscall.Dup(fd)
}

View File

@@ -0,0 +1,7 @@
package libbox
import "os"
func dup(fd int) (nfd int, err error) {
return 0, os.ErrInvalid
}

View File

@@ -1,5 +1,3 @@
//go:build linux || darwin
package libbox package libbox
import ( import (

View File

@@ -1,5 +1,3 @@
//go:build linux || darwin
package libbox package libbox
import ( import (

View File

@@ -3,11 +3,10 @@ package trackerconn
import ( import (
"net" "net"
"github.com/sagernet/sing/common/atomic"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"go.uber.org/atomic"
) )
func New(conn net.Conn, readCounter []*atomic.Int64, writeCounter []*atomic.Int64) *Conn { func New(conn net.Conn, readCounter []*atomic.Int64, writeCounter []*atomic.Int64) *Conn {

View File

@@ -1,11 +1,10 @@
package trackerconn package trackerconn
import ( import (
"github.com/sagernet/sing/common/atomic"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"go.uber.org/atomic"
) )
func NewPacket(conn N.PacketConn, readCounter []*atomic.Int64, writeCounter []*atomic.Int64) *PacketConn { func NewPacket(conn N.PacketConn, readCounter []*atomic.Int64, writeCounter []*atomic.Int64) *PacketConn {

View File

@@ -12,10 +12,9 @@ import (
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/experimental/trackerconn" "github.com/sagernet/sing-box/experimental/trackerconn"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common/atomic"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"go.uber.org/atomic"
) )
func init() { func init() {
@@ -211,7 +210,7 @@ func (s *StatsService) loadOrCreateCounter(name string) *atomic.Int64 {
if loaded { if loaded {
return counter return counter
} }
counter = atomic.NewInt64(0) counter = &atomic.Int64{}
s.counters[name] = counter s.counters[name] = counter
return counter return counter
} }

27
go.mod
View File

@@ -4,7 +4,7 @@ go 1.18
require ( require (
berty.tech/go-libtor v1.0.385 berty.tech/go-libtor v1.0.385
github.com/Dreamacro/clash v1.14.0 github.com/Dreamacro/clash v1.15.0
github.com/caddyserver/certmagic v0.17.2 github.com/caddyserver/certmagic v0.17.2
github.com/cretz/bine v0.2.0 github.com/cretz/bine v0.2.0
github.com/dustin/go-humanize v1.0.1 github.com/dustin/go-humanize v1.0.1
@@ -13,8 +13,9 @@ require (
github.com/go-chi/cors v1.2.1 github.com/go-chi/cors v1.2.1
github.com/go-chi/render v1.0.2 github.com/go-chi/render v1.0.2
github.com/gofrs/uuid v4.4.0+incompatible github.com/gofrs/uuid v4.4.0+incompatible
github.com/gofrs/uuid/v5 v5.0.0
github.com/hashicorp/yamux v0.1.1 github.com/hashicorp/yamux v0.1.1
github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961 github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16
github.com/logrusorgru/aurora v2.0.3+incompatible github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/mholt/acmez v1.1.0 github.com/mholt/acmez v1.1.0
github.com/miekg/dns v1.1.53 github.com/miekg/dns v1.1.53
@@ -22,11 +23,11 @@ require (
github.com/oschwald/maxminddb-golang v1.10.0 github.com/oschwald/maxminddb-golang v1.10.0
github.com/pires/go-proxyproto v0.7.0 github.com/pires/go-proxyproto v0.7.0
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0
github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
github.com/sagernet/reality v0.0.0-20230323230523-5fa25e693e7f github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
github.com/sagernet/sing v0.2.1 github.com/sagernet/sing v0.2.3
github.com/sagernet/sing-dns v0.1.5-0.20230331013337-06044a57b1da github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc
github.com/sagernet/sing-shadowsocks v0.2.0 github.com/sagernet/sing-shadowsocks v0.2.0
github.com/sagernet/sing-shadowtls v0.1.0 github.com/sagernet/sing-shadowtls v0.1.0
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab
@@ -36,16 +37,15 @@ require (
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c
github.com/spf13/cobra v1.6.1 github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.2 github.com/stretchr/testify v1.8.2
go.etcd.io/bbolt v1.3.7 go.etcd.io/bbolt v1.3.7
go.uber.org/atomic v1.10.0
go.uber.org/zap v1.24.0 go.uber.org/zap v1.24.0
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35 go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35
golang.org/x/crypto v0.7.0 golang.org/x/crypto v0.8.0
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 golang.org/x/exp v0.0.0-20230321023759-10a507213a29
golang.org/x/net v0.8.0 golang.org/x/net v0.9.0
golang.org/x/sys v0.6.0 golang.org/x/sys v0.7.0
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230215201556-9c5414ab4bde golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230215201556-9c5414ab4bde
google.golang.org/grpc v1.54.0 google.golang.org/grpc v1.54.0
google.golang.org/protobuf v1.30.0 google.golang.org/protobuf v1.30.0
@@ -64,7 +64,7 @@ require (
github.com/golang/protobuf v1.5.2 // indirect github.com/golang/protobuf v1.5.2 // indirect
github.com/google/btree v1.0.1 // indirect github.com/google/btree v1.0.1 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/native v1.1.0 // indirect github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/compress v1.15.15 // indirect github.com/klauspost/compress v1.15.15 // indirect
github.com/klauspost/cpuid/v2 v2.1.1 // indirect github.com/klauspost/cpuid/v2 v2.1.1 // indirect
@@ -82,9 +82,10 @@ require (
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // 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/multierr v1.6.0 // indirect
golang.org/x/mod v0.8.0 // indirect golang.org/x/mod v0.8.0 // indirect
golang.org/x/text v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
golang.org/x/tools v0.6.0 // indirect golang.org/x/tools v0.6.0 // indirect
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect

52
go.sum
View File

@@ -1,7 +1,7 @@
berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw= berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw=
berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw= berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw=
github.com/Dreamacro/clash v1.14.0 h1:ehJ/C/1m9LEjmME72WSE/Y2YqbR3Q54AbjqiRCvtyW4= github.com/Dreamacro/clash v1.15.0 h1:mlpD950VEggXZBNahV66hyKDRxcczkj3vymoAt78KyE=
github.com/Dreamacro/clash v1.14.0/go.mod h1:ia2CU7V713H1QdCqMwOLK9U9V5Ay8X0voj3yQr2tk+I= github.com/Dreamacro/clash v1.15.0/go.mod h1:WNH69bN11LiAdgdSr4hpkEuXVMfBbWyhEKMCTx9BtNE=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
@@ -35,6 +35,8 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8Wd
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
@@ -49,10 +51,10 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961 h1:x/YtdDlmypenG1te/FfH6LVM+3krhXk5CFV8VYNNX5M= github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 h1:+aAGyK41KRn8jbF2Q7PLL0Sxwg6dShGcQSeCC7nZQ8E=
github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI= github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI=
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
@@ -101,20 +103,20 @@ github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 h1:KyhtFFt
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I= github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
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 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms= github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca h1:w56+kf8BeqLqllrRJ1tdwKc3sCdWOn/DuNHpY9fAiqs= github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035 h1:KttYh6bBhIw8Y6/Ljn7CGwC3CKZn788rzMJmeAKjY+8=
github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU= github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY= github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY=
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32/go.mod h1:QMCkxXAC3CvBgDZVIJp43NWTuwGBScCzMLVLynjERL8= github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32/go.mod h1:QMCkxXAC3CvBgDZVIJp43NWTuwGBScCzMLVLynjERL8=
github.com/sagernet/reality v0.0.0-20230323230523-5fa25e693e7f h1:plVtFF9NVw5Py4jH/KQuWxojdMFDroTsQ1PVJcU9djM= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
github.com/sagernet/reality v0.0.0-20230323230523-5fa25e693e7f/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
github.com/sagernet/sing v0.2.1 h1:r0STYeyfKBBtoAHsBtW1dQonxG+3Qidde7/1VAMhdn8= github.com/sagernet/sing v0.2.3 h1:V50MvZ4c3Iij2lYFWPlzL1PyipwSzjGeN9x+Ox89vpk=
github.com/sagernet/sing v0.2.1/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw= github.com/sagernet/sing v0.2.3/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
github.com/sagernet/sing-dns v0.1.5-0.20230331013337-06044a57b1da h1:pZV4DRBArbgkajeCZWn3VqwLF+Wl7HOlAt5aSJuuKDk= github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc h1:hmbuqKv48SAjiKPoqtJGvS5pEHVPZjTHq9CPwQY2cZ4=
github.com/sagernet/sing-dns v0.1.5-0.20230331013337-06044a57b1da/go.mod h1:8x+rlRnPE/5/IagjlAUqR9TceRYRL2WyqmP5QYK3dkI= github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc/go.mod h1:ZKuuqgsHRxDahYrzgSgy4vIAGGuKPlIf4hLcNzYzLkY=
github.com/sagernet/sing-shadowsocks v0.2.0 h1:ILDWL7pwWfkPLEbviE/MyCgfjaBmJY/JVVY+5jhSb58= github.com/sagernet/sing-shadowsocks v0.2.0 h1:ILDWL7pwWfkPLEbviE/MyCgfjaBmJY/JVVY+5jhSb58=
github.com/sagernet/sing-shadowsocks v0.2.0/go.mod h1:ysYzszRLpNzJSorvlWRMuzU6Vchsp7sd52q+JNY4axw= github.com/sagernet/sing-shadowsocks v0.2.0/go.mod h1:ysYzszRLpNzJSorvlWRMuzU6Vchsp7sd52q+JNY4axw=
github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ= github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ=
@@ -133,8 +135,8 @@ github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+V
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY= github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo= github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI= github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -168,8 +170,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -180,8 +182,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 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-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
@@ -199,15 +201,15 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@@ -10,11 +10,10 @@ import (
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/atomic"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"go.uber.org/atomic"
) )
var _ adapter.Inbound = (*myInboundAdapter)(nil) var _ adapter.Inbound = (*myInboundAdapter)(nil)

View File

@@ -12,7 +12,7 @@ import (
) )
func init() { func init() {
dns.RegisterTransport([]string{"dhcp"}, func(ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) { dns.RegisterTransport([]string{"dhcp"}, func(name string, ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
return nil, E.New(`DHCP is not included in this build, rebuild with -tags with_dhcp`) return nil, E.New(`DHCP is not included in this build, rebuild with -tags with_dhcp`)
}) })
} }

View File

@@ -19,7 +19,7 @@ import (
const WithQUIC = false const WithQUIC = false
func init() { func init() {
dns.RegisterTransport([]string{"quic", "h3"}, func(ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) { dns.RegisterTransport([]string{"quic", "h3"}, func(name string, ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
return nil, C.ErrQUICNotIncluded return nil, C.ErrQUICNotIncluded
}) })
v2ray.RegisterQUICConstructor( v2ray.RegisterQUICConstructor(

View File

@@ -49,6 +49,10 @@ func (f *simpleFactory) NewLogger(tag string) ContextLogger {
return &simpleLogger{f, tag} return &simpleLogger{f, tag}
} }
func (f *simpleFactory) Close() error {
return nil
}
var _ ContextLogger = (*simpleLogger)(nil) var _ ContextLogger = (*simpleLogger)(nil)
type simpleLogger struct { type simpleLogger struct {

View File

@@ -15,6 +15,7 @@ type Factory interface {
SetLevel(level Level) SetLevel(level Level)
Logger() ContextLogger Logger() ContextLogger
NewLogger(tag string) ContextLogger NewLogger(tag string) ContextLogger
Close() error
} }
type ObservableFactory interface { type ObservableFactory interface {

110
log/log.go Normal file
View File

@@ -0,0 +1,110 @@
package log
import (
"io"
"os"
"time"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
)
type factoryWithFile struct {
Factory
file *os.File
}
func (f *factoryWithFile) Close() error {
return common.Close(
f.Factory,
common.PtrOrNil(f.file),
)
}
type observableFactoryWithFile struct {
ObservableFactory
file *os.File
}
func (f *observableFactoryWithFile) Close() error {
return common.Close(
f.ObservableFactory,
common.PtrOrNil(f.file),
)
}
type Options struct {
Options option.LogOptions
Observable bool
DefaultWriter io.Writer
BaseTime time.Time
PlatformWriter io.Writer
}
func New(options Options) (Factory, error) {
logOptions := options.Options
if logOptions.Disabled {
return NewNOPFactory(), nil
}
var logFile *os.File
var logWriter io.Writer
switch logOptions.Output {
case "":
logWriter = options.DefaultWriter
if logWriter == nil {
logWriter = os.Stderr
}
case "stderr":
logWriter = os.Stderr
case "stdout":
logWriter = os.Stdout
default:
var err error
logFile, err = os.OpenFile(C.BasePath(logOptions.Output), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
return nil, err
}
logWriter = logFile
}
logFormatter := Formatter{
BaseTime: options.BaseTime,
DisableColors: logOptions.DisableColor || logFile != nil,
DisableTimestamp: !logOptions.Timestamp && logFile != nil,
FullTimestamp: logOptions.Timestamp,
TimestampFormat: "-0700 2006-01-02 15:04:05",
}
var factory Factory
if options.Observable {
factory = NewObservableFactory(logFormatter, logWriter, options.PlatformWriter)
} else {
factory = NewFactory(logFormatter, logWriter, options.PlatformWriter)
}
if logOptions.Level != "" {
logLevel, err := ParseLevel(logOptions.Level)
if err != nil {
return nil, E.Cause(err, "parse log level")
}
factory.SetLevel(logLevel)
} else {
factory.SetLevel(LevelTrace)
}
if logFile != nil {
if options.Observable {
factory = &observableFactoryWithFile{
ObservableFactory: factory.(ObservableFactory),
file: logFile,
}
} else {
factory = &factoryWithFile{
Factory: factory,
file: logFile,
}
}
}
return factory, nil
}

View File

@@ -72,6 +72,10 @@ func (f *nopFactory) FatalContext(ctx context.Context, args ...any) {
func (f *nopFactory) PanicContext(ctx context.Context, args ...any) { func (f *nopFactory) PanicContext(ctx context.Context, args ...any) {
} }
func (f *nopFactory) Close() error {
return nil
}
func (f *nopFactory) Subscribe() (subscription observable.Subscription[Entry], done <-chan struct{}, err error) { func (f *nopFactory) Subscribe() (subscription observable.Subscription[Entry], done <-chan struct{}, err error) {
return nil, nil, os.ErrInvalid return nil, nil, os.ErrInvalid
} }

View File

@@ -38,8 +38,8 @@ nav:
- Installation: - Installation:
- From source: installation/from-source.md - From source: installation/from-source.md
- Clients: - Clients:
- SFI: - iOS: installation/clients/sfi.md
- installation/clients/sfi/index.md - Android: installation/clients/sfa.md
- Configuration: - Configuration:
- configuration/index.md - configuration/index.md
- Log: - Log:

View File

@@ -2,6 +2,7 @@ package ntp
import ( import (
"context" "context"
"os"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@@ -9,6 +10,7 @@ import (
"github.com/sagernet/sing-box/common/settings" "github.com/sagernet/sing-box/common/settings"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger" "github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
@@ -20,7 +22,7 @@ var _ adapter.TimeService = (*Service)(nil)
type Service struct { type Service struct {
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel common.ContextCancelCauseFunc
server M.Socksaddr server M.Socksaddr
writeToSystem bool writeToSystem bool
dialer N.Dialer dialer N.Dialer
@@ -30,7 +32,7 @@ type Service struct {
} }
func NewService(ctx context.Context, router adapter.Router, logger logger.Logger, options option.NTPOptions) *Service { func NewService(ctx context.Context, router adapter.Router, logger logger.Logger, options option.NTPOptions) *Service {
ctx, cancel := context.WithCancel(ctx) ctx, cancel := common.ContextWithCancelCause(ctx)
server := options.ServerOptions.Build() server := options.ServerOptions.Build()
if server.Port == 0 { if server.Port == 0 {
server.Port = 123 server.Port = 123
@@ -64,7 +66,7 @@ func (s *Service) Start() error {
func (s *Service) Close() error { func (s *Service) Close() error {
s.ticker.Stop() s.ticker.Stop()
s.cancel() s.cancel(os.ErrClosed)
return nil return nil
} }

View File

@@ -61,19 +61,19 @@ func (o *V2RayTransportOptions) UnmarshalJSON(bytes []byte) error {
} }
type V2RayHTTPOptions struct { type V2RayHTTPOptions struct {
Host Listable[string] `json:"host,omitempty"` Host Listable[string] `json:"host,omitempty"`
Path string `json:"path,omitempty"` Path string `json:"path,omitempty"`
Method string `json:"method,omitempty"` Method string `json:"method,omitempty"`
Headers map[string]string `json:"headers,omitempty"` Headers map[string]Listable[string] `json:"headers,omitempty"`
IdleTimeout Duration `json:"idle_timeout,omitempty"` IdleTimeout Duration `json:"idle_timeout,omitempty"`
PingTimeout Duration `json:"ping_timeout,omitempty"` PingTimeout Duration `json:"ping_timeout,omitempty"`
} }
type V2RayWebsocketOptions struct { type V2RayWebsocketOptions struct {
Path string `json:"path,omitempty"` Path string `json:"path,omitempty"`
Headers map[string]string `json:"headers,omitempty"` Headers map[string]Listable[string] `json:"headers,omitempty"`
MaxEarlyData uint32 `json:"max_early_data,omitempty"` MaxEarlyData uint32 `json:"max_early_data,omitempty"`
EarlyDataHeaderName string `json:"early_data_header_name,omitempty"` EarlyDataHeaderName string `json:"early_data_header_name,omitempty"`
} }
type V2RayQUICOptions struct{} type V2RayQUICOptions struct{}

View File

@@ -10,50 +10,51 @@ import (
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
) )
func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.Outbound) (adapter.Outbound, error) { func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Outbound) (adapter.Outbound, error) {
var metadata *adapter.InboundContext var metadata *adapter.InboundContext
if options.Tag != "" { if tag != "" {
ctx, metadata = adapter.AppendContext(ctx) ctx, metadata = adapter.AppendContext(ctx)
metadata.Outbound = options.Tag metadata.Outbound = tag
} }
if options.Type == "" { if options.Type == "" {
return nil, E.New("missing outbound type") return nil, E.New("missing outbound type")
} }
ctx = ContextWithTag(ctx, tag)
switch options.Type { switch options.Type {
case C.TypeDirect: case C.TypeDirect:
return NewDirect(router, logger, options.Tag, options.DirectOptions) return NewDirect(router, logger, tag, options.DirectOptions)
case C.TypeBlock: case C.TypeBlock:
return NewBlock(logger, options.Tag), nil return NewBlock(logger, tag), nil
case C.TypeDNS: case C.TypeDNS:
return NewDNS(router, options.Tag), nil return NewDNS(router, tag), nil
case C.TypeSocks: case C.TypeSocks:
return NewSocks(router, logger, options.Tag, options.SocksOptions) return NewSocks(router, logger, tag, options.SocksOptions)
case C.TypeHTTP: case C.TypeHTTP:
return NewHTTP(router, logger, options.Tag, options.HTTPOptions) return NewHTTP(router, logger, tag, options.HTTPOptions)
case C.TypeShadowsocks: case C.TypeShadowsocks:
return NewShadowsocks(ctx, router, logger, options.Tag, options.ShadowsocksOptions) return NewShadowsocks(ctx, router, logger, tag, options.ShadowsocksOptions)
case C.TypeVMess: case C.TypeVMess:
return NewVMess(ctx, router, logger, options.Tag, options.VMessOptions) return NewVMess(ctx, router, logger, tag, options.VMessOptions)
case C.TypeTrojan: case C.TypeTrojan:
return NewTrojan(ctx, router, logger, options.Tag, options.TrojanOptions) return NewTrojan(ctx, router, logger, tag, options.TrojanOptions)
case C.TypeWireGuard: case C.TypeWireGuard:
return NewWireGuard(ctx, router, logger, options.Tag, options.WireGuardOptions) return NewWireGuard(ctx, router, logger, tag, options.WireGuardOptions)
case C.TypeHysteria: case C.TypeHysteria:
return NewHysteria(ctx, router, logger, options.Tag, options.HysteriaOptions) return NewHysteria(ctx, router, logger, tag, options.HysteriaOptions)
case C.TypeTor: case C.TypeTor:
return NewTor(ctx, router, logger, options.Tag, options.TorOptions) return NewTor(ctx, router, logger, tag, options.TorOptions)
case C.TypeSSH: case C.TypeSSH:
return NewSSH(ctx, router, logger, options.Tag, options.SSHOptions) return NewSSH(ctx, router, logger, tag, options.SSHOptions)
case C.TypeShadowTLS: case C.TypeShadowTLS:
return NewShadowTLS(ctx, router, logger, options.Tag, options.ShadowTLSOptions) return NewShadowTLS(ctx, router, logger, tag, options.ShadowTLSOptions)
case C.TypeShadowsocksR: case C.TypeShadowsocksR:
return NewShadowsocksR(ctx, router, logger, options.Tag, options.ShadowsocksROptions) return NewShadowsocksR(ctx, router, logger, tag, options.ShadowsocksROptions)
case C.TypeVLESS: case C.TypeVLESS:
return NewVLESS(ctx, router, logger, options.Tag, options.VLESSOptions) return NewVLESS(ctx, router, logger, tag, options.VLESSOptions)
case C.TypeSelector: case C.TypeSelector:
return NewSelector(router, logger, options.Tag, options.SelectorOptions) return NewSelector(router, logger, tag, options.SelectorOptions)
case C.TypeURLTest: case C.TypeURLTest:
return NewURLTest(router, logger, options.Tag, options.URLTestOptions) return NewURLTest(router, logger, tag, options.URLTestOptions)
default: default:
return nil, E.New("unknown outbound type: ", options.Type) return nil, E.New("unknown outbound type: ", options.Type)
} }

View File

@@ -3,6 +3,7 @@ package outbound
import ( import (
"context" "context"
"net" "net"
"net/netip"
"os" "os"
"runtime" "runtime"
"time" "time"
@@ -56,15 +57,21 @@ func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata a
func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext) error { func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext) error {
ctx = adapter.WithContext(ctx, &metadata) ctx = adapter.WithContext(ctx, &metadata)
var outConn net.PacketConn var outConn net.PacketConn
var destinationAddress netip.Addr
var err error var err error
if len(metadata.DestinationAddresses) > 0 { if len(metadata.DestinationAddresses) > 0 {
outConn, err = N.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses) outConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses)
} else { } else {
outConn, err = this.ListenPacket(ctx, metadata.Destination) outConn, err = this.ListenPacket(ctx, metadata.Destination)
} }
if err != nil { if err != nil {
return N.HandshakeFailure(conn, err) return N.HandshakeFailure(conn, err)
} }
if destinationAddress.IsValid() {
if natConn, loaded := common.Cast[bufio.NATPacketConn](conn); loaded {
natConn.UpdateDestination(destinationAddress)
}
}
switch metadata.Protocol { switch metadata.Protocol {
case C.ProtocolSTUN: case C.ProtocolSTUN:
ctx, conn = canceler.NewPacketConn(ctx, conn, C.STUNTimeout) ctx, conn = canceler.NewPacketConn(ctx, conn, C.STUNTimeout)

View File

@@ -102,11 +102,10 @@ func (d *DNS) handleConnection(ctx context.Context, conn net.Conn, metadata adap
func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
ctx = adapter.WithContext(ctx, &metadata) ctx = adapter.WithContext(ctx, &metadata)
fastClose, cancel := context.WithCancel(ctx) fastClose, cancel := common.ContextWithCancelCause(ctx)
timeout := canceler.New(fastClose, cancel, C.DNSTimeout) timeout := canceler.New(fastClose, cancel, C.DNSTimeout)
var group task.Group var group task.Group
group.Append0(func(ctx context.Context) error { group.Append0(func(ctx context.Context) error {
defer cancel()
_buffer := buf.StackNewSize(dns.FixedPacketSize) _buffer := buf.StackNewSize(dns.FixedPacketSize)
defer common.KeepAlive(_buffer) defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer) buffer := common.Dup(_buffer)
@@ -115,11 +114,13 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada
buffer.FullReset() buffer.FullReset()
destination, err := conn.ReadPacket(buffer) destination, err := conn.ReadPacket(buffer)
if err != nil { if err != nil {
cancel(err)
return err return err
} }
var message mDNS.Msg var message mDNS.Msg
err = message.Unpack(buffer.Bytes()) err = message.Unpack(buffer.Bytes())
if err != nil { if err != nil {
cancel(err)
return err return err
} }
timeout.Update() timeout.Update()
@@ -127,17 +128,22 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada
go func() error { go func() error {
response, err := d.router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message) response, err := d.router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message)
if err != nil { if err != nil {
cancel(err)
return err return err
} }
timeout.Update() timeout.Update()
responseBuffer := buf.NewPacket() responseBuffer := buf.NewPacket()
n, err := response.PackBuffer(responseBuffer.FreeBytes()) n, err := response.PackBuffer(responseBuffer.FreeBytes())
if err != nil { if err != nil {
cancel(err)
responseBuffer.Release() responseBuffer.Release()
return err return err
} }
responseBuffer.Truncate(len(n)) responseBuffer.Truncate(len(n))
err = conn.WritePacket(responseBuffer, destination) err = conn.WritePacket(responseBuffer, destination)
if err != nil {
cancel(err)
}
return err return err
}() }()
} }

View File

@@ -14,14 +14,14 @@ import (
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/protocol/http" sHTTP "github.com/sagernet/sing/protocol/http"
) )
var _ adapter.Outbound = (*HTTP)(nil) var _ adapter.Outbound = (*HTTP)(nil)
type HTTP struct { type HTTP struct {
myOutboundAdapter myOutboundAdapter
client *http.Client client *sHTTP.Client
} }
func NewHTTP(router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) (*HTTP, error) { func NewHTTP(router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) (*HTTP, error) {
@@ -37,7 +37,12 @@ func NewHTTP(router adapter.Router, logger log.ContextLogger, tag string, option
logger: logger, logger: logger,
tag: tag, tag: tag,
}, },
http.NewClient(detour, options.ServerOptions.Build(), options.Username, options.Password), sHTTP.NewClient(sHTTP.Options{
Dialer: detour,
Server: options.ServerOptions.Build(),
Username: options.Username,
Password: options.Password,
}),
}, nil }, nil
} }

14
outbound/lookback.go Normal file
View File

@@ -0,0 +1,14 @@
package outbound
import "context"
type outboundTagKey struct{}
func ContextWithTag(ctx context.Context, outboundTag string) context.Context {
return context.WithValue(ctx, outboundTagKey{}, outboundTag)
}
func TagFromContext(ctx context.Context) (string, bool) {
value, loaded := ctx.Value(outboundTagKey{}).(string)
return value, loaded
}

View File

@@ -157,7 +157,7 @@ func (h *shadowsocksDialer) DialContext(ctx context.Context, network string, des
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &bufio.BindPacketConn{PacketConn: h.method.DialPacketConn(outConn), Addr: destination}, nil return bufio.NewBindPacketConn(h.method.DialPacketConn(outConn), destination), nil
default: default:
return nil, E.Extend(N.ErrUnknownNetwork, network) return nil, E.Extend(N.ErrUnknownNetwork, network)
} }

View File

@@ -131,7 +131,7 @@ func (h *trojanDialer) DialContext(ctx context.Context, network string, destinat
case N.NetworkTCP: case N.NetworkTCP:
return trojan.NewClientConn(conn, h.key, destination), nil return trojan.NewClientConn(conn, h.key, destination), nil
case N.NetworkUDP: case N.NetworkUDP:
return &bufio.BindPacketConn{PacketConn: trojan.NewClientPacketConn(conn, h.key), Addr: destination}, nil return bufio.NewBindPacketConn(trojan.NewClientPacketConn(conn, h.key), destination), nil
default: default:
return nil, E.Extend(N.ErrUnknownNetwork, network) return nil, E.Extend(N.ErrUnknownNetwork, network)
} }

View File

@@ -12,7 +12,6 @@ import (
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/v2ray" "github.com/sagernet/sing-box/transport/v2ray"
"github.com/sagernet/sing-box/transport/vless" "github.com/sagernet/sing-box/transport/vless"
"github.com/sagernet/sing-dns"
"github.com/sagernet/sing-vmess/packetaddr" "github.com/sagernet/sing-vmess/packetaddr"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
@@ -105,11 +104,14 @@ func (h *VLESS) DialContext(ctx context.Context, network string, destination M.S
if h.xudp { if h.xudp {
return h.client.DialEarlyXUDPPacketConn(conn, destination) return h.client.DialEarlyXUDPPacketConn(conn, destination)
} else if h.packetAddr { } else if h.packetAddr {
if destination.IsFqdn() {
return nil, E.New("packetaddr: domain destination is not supported")
}
packetConn, err := h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}) packetConn, err := h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress})
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &bufio.BindPacketConn{PacketConn: dialer.NewResolvePacketConn(ctx, h.router, dns.DomainStrategyAsIS, packetaddr.NewConn(packetConn, destination)), Addr: destination}, nil return bufio.NewBindPacketConn(packetaddr.NewConn(packetConn, destination), destination), nil
} else { } else {
return h.client.DialEarlyPacketConn(conn, destination) return h.client.DialEarlyPacketConn(conn, destination)
} }
@@ -140,11 +142,14 @@ func (h *VLESS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.
if h.xudp { if h.xudp {
return h.client.DialEarlyXUDPPacketConn(conn, destination) return h.client.DialEarlyXUDPPacketConn(conn, destination)
} else if h.packetAddr { } else if h.packetAddr {
if destination.IsFqdn() {
return nil, E.New("packetaddr: domain destination is not supported")
}
conn, err := h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}) conn, err := h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress})
if err != nil { if err != nil {
return nil, err return nil, err
} }
return dialer.NewResolvePacketConn(ctx, h.router, dns.DomainStrategyAsIS, packetaddr.NewConn(conn, destination)), nil return packetaddr.NewConn(conn, destination), nil
} else { } else {
return h.client.DialEarlyPacketConn(conn, destination) return h.client.DialEarlyPacketConn(conn, destination)
} }

View File

@@ -12,7 +12,6 @@ import (
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/v2ray" "github.com/sagernet/sing-box/transport/v2ray"
"github.com/sagernet/sing-dns"
"github.com/sagernet/sing-vmess" "github.com/sagernet/sing-vmess"
"github.com/sagernet/sing-vmess/packetaddr" "github.com/sagernet/sing-vmess/packetaddr"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
@@ -188,7 +187,10 @@ func (h *vmessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr)
return nil, err return nil, err
} }
if h.packetAddr { if h.packetAddr {
return dialer.NewResolvePacketConn(ctx, h.router, dns.DomainStrategyAsIS, packetaddr.NewConn(h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}), destination)), nil if destination.IsFqdn() {
return nil, E.New("packetaddr: domain destination is not supported")
}
return packetaddr.NewConn(h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}), destination), nil
} else if h.xudp { } else if h.xudp {
return h.client.DialEarlyXUDPPacketConn(conn, destination), nil return h.client.DialEarlyXUDPPacketConn(conn, destination), nil
} else { } else {

View File

@@ -179,5 +179,6 @@ func (w *WireGuard) Close() error {
if w.device != nil { if w.device != nil {
w.device.Close() w.device.Close()
} }
return common.Close(w.tunDevice) w.tunDevice.Close()
return nil
} }

View File

@@ -26,6 +26,7 @@ import (
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/ntp" "github.com/sagernet/sing-box/ntp"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/outbound"
"github.com/sagernet/sing-dns" "github.com/sagernet/sing-dns"
"github.com/sagernet/sing-tun" "github.com/sagernet/sing-tun"
"github.com/sagernet/sing-vmess" "github.com/sagernet/sing-vmess"
@@ -218,7 +219,7 @@ func NewRouter(
} }
} }
} }
transport, err := dns.CreateTransport(ctx, logFactory.NewLogger(F.ToString("dns/transport[", tag, "]")), detour, server.Address) transport, err := dns.CreateTransport(tag, ctx, logFactory.NewLogger(F.ToString("dns/transport[", tag, "]")), detour, server.Address)
if err != nil { if err != nil {
return nil, E.Cause(err, "parse dns server[", tag, "]") return nil, E.Cause(err, "parse dns server[", tag, "]")
} }
@@ -258,7 +259,7 @@ func NewRouter(
} }
if defaultTransport == nil { if defaultTransport == nil {
if len(transports) == 0 { if len(transports) == 0 {
transports = append(transports, dns.NewLocalTransport(N.SystemDialer)) transports = append(transports, dns.NewLocalTransport("local", N.SystemDialer))
} }
defaultTransport = transports[0] defaultTransport = transports[0]
} }
@@ -489,31 +490,56 @@ func (r *Router) Start() error {
} }
func (r *Router) Close() error { func (r *Router) Close() error {
for _, rule := range r.rules { var err error
err := rule.Close() for i, rule := range r.rules {
if err != nil { r.logger.Trace("closing rule[", i, "]")
return err err = E.Append(err, rule.Close(), func(err error) error {
} return E.Cause(err, "close rule[", i, "]")
})
} }
for _, rule := range r.dnsRules { for i, rule := range r.dnsRules {
err := rule.Close() r.logger.Trace("closing dns rule[", i, "]")
if err != nil { err = E.Append(err, rule.Close(), func(err error) error {
return err return E.Cause(err, "close dns rule[", i, "]")
} })
} }
for _, transport := range r.transports { for i, transport := range r.transports {
err := transport.Close() r.logger.Trace("closing transport[", i, "] ")
if err != nil { err = E.Append(err, transport.Close(), func(err error) error {
return err return E.Cause(err, "close dns transport[", i, "]")
} })
} }
return common.Close( if r.geositeReader != nil {
common.PtrOrNil(r.geoIPReader), r.logger.Trace("closing geoip reader")
r.interfaceMonitor, err = E.Append(err, common.Close(r.geoIPReader), func(err error) error {
r.networkMonitor, return E.Cause(err, "close geoip reader")
r.packageManager, })
r.timeService, }
) if r.interfaceMonitor != nil {
r.logger.Trace("closing interface monitor")
err = E.Append(err, r.interfaceMonitor.Close(), func(err error) error {
return E.Cause(err, "close interface monitor")
})
}
if r.networkMonitor != nil {
r.logger.Trace("closing network monitor")
err = E.Append(err, r.networkMonitor.Close(), func(err error) error {
return E.Cause(err, "close network monitor")
})
}
if r.packageManager != nil {
r.logger.Trace("closing package manager")
err = E.Append(err, r.packageManager.Close(), func(err error) error {
return E.Cause(err, "close package manager")
})
}
if r.timeService != nil {
r.logger.Trace("closing time service")
err = E.Append(err, r.timeService.Close(), func(err error) error {
return E.Cause(err, "close time service")
})
}
return err
} }
func (r *Router) GeoIPReader() *geoip.Reader { func (r *Router) GeoIPReader() *geoip.Reader {
@@ -605,7 +631,7 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
if metadata.InboundOptions.SniffEnabled { if metadata.InboundOptions.SniffEnabled {
buffer := buf.NewPacket() buffer := buf.NewPacket()
buffer.FullReset() buffer.FullReset()
sniffMetadata, _ := sniff.PeekStream(ctx, conn, buffer, time.Duration(metadata.InboundOptions.SniffTimeout), sniff.StreamDomainNameQuery, sniff.TLSClientHello, sniff.HTTPHost) sniffMetadata, err := sniff.PeekStream(ctx, conn, buffer, time.Duration(metadata.InboundOptions.SniffTimeout), sniff.StreamDomainNameQuery, sniff.TLSClientHello, sniff.HTTPHost)
if sniffMetadata != nil { if sniffMetadata != nil {
metadata.Protocol = sniffMetadata.Protocol metadata.Protocol = sniffMetadata.Protocol
metadata.Domain = sniffMetadata.Domain metadata.Domain = sniffMetadata.Domain
@@ -620,6 +646,8 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
} else { } else {
r.logger.DebugContext(ctx, "sniffed protocol: ", metadata.Protocol) r.logger.DebugContext(ctx, "sniffed protocol: ", metadata.Protocol)
} }
} else if err != nil {
r.logger.TraceContext(ctx, "sniffed no protocol: ", err)
} }
if !buffer.IsEmpty() { if !buffer.IsEmpty() {
conn = bufio.NewCachedConn(conn, buffer) conn = bufio.NewCachedConn(conn, buffer)
@@ -635,9 +663,11 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
metadata.DestinationAddresses = addresses metadata.DestinationAddresses = addresses
r.dnsLogger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]") r.dnsLogger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]")
} }
matchedRule, detour := r.match(ctx, &metadata, r.defaultOutboundForConnection) ctx, matchedRule, detour, err := r.match(ctx, &metadata, r.defaultOutboundForConnection)
if err != nil {
return err
}
if !common.Contains(detour.Network(), N.NetworkTCP) { if !common.Contains(detour.Network(), N.NetworkTCP) {
conn.Close()
return E.New("missing supported outbound, closing connection") return E.New("missing supported outbound, closing connection")
} }
if r.clashServer != nil { if r.clashServer != nil {
@@ -713,9 +743,11 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
metadata.DestinationAddresses = addresses metadata.DestinationAddresses = addresses
r.dnsLogger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]") r.dnsLogger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]")
} }
matchedRule, detour := r.match(ctx, &metadata, r.defaultOutboundForPacketConnection) ctx, matchedRule, detour, err := r.match(ctx, &metadata, r.defaultOutboundForPacketConnection)
if err != nil {
return err
}
if !common.Contains(detour.Network(), N.NetworkUDP) { if !common.Contains(detour.Network(), N.NetworkUDP) {
conn.Close()
return E.New("missing supported outbound, closing packet connection") return E.New("missing supported outbound, closing packet connection")
} }
if r.clashServer != nil { if r.clashServer != nil {
@@ -731,7 +763,18 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
return detour.NewPacketConnection(ctx, conn, metadata) return detour.NewPacketConnection(ctx, conn, metadata)
} }
func (r *Router) match(ctx context.Context, metadata *adapter.InboundContext, defaultOutbound adapter.Outbound) (adapter.Rule, adapter.Outbound) { func (r *Router) match(ctx context.Context, metadata *adapter.InboundContext, defaultOutbound adapter.Outbound) (context.Context, adapter.Rule, adapter.Outbound, error) {
matchRule, matchOutbound := r.match0(ctx, metadata, defaultOutbound)
if contextOutbound, loaded := outbound.TagFromContext(ctx); loaded {
if contextOutbound == matchOutbound.Tag() {
return nil, nil, nil, E.New("connection loopback in outbound/", matchOutbound.Type(), "[", matchOutbound.Tag(), "]")
}
}
ctx = outbound.ContextWithTag(ctx, matchOutbound.Tag())
return ctx, matchRule, matchOutbound, nil
}
func (r *Router) match0(ctx context.Context, metadata *adapter.InboundContext, defaultOutbound adapter.Outbound) (adapter.Rule, adapter.Outbound) {
if r.processSearcher != nil { if r.processSearcher != nil {
var originDestination netip.AddrPort var originDestination netip.AddrPort
if metadata.OriginDestination.IsValid() { if metadata.OriginDestination.IsValid() {

View File

@@ -37,7 +37,10 @@ func startInstance(t *testing.T, options option.Options) *box.Box {
var instance *box.Box var instance *box.Box
var err error var err error
for retry := 0; retry < 3; retry++ { for retry := 0; retry < 3; retry++ {
instance, err = box.New(ctx, options, nil) instance, err = box.New(box.Options{
Context: ctx,
Options: options,
})
require.NoError(t, err) require.NoError(t, err)
err = instance.Start() err = instance.Start()
if err != nil { if err != nil {

View File

@@ -10,17 +10,17 @@ require (
github.com/docker/docker v20.10.18+incompatible github.com/docker/docker v20.10.18+incompatible
github.com/docker/go-connections v0.4.0 github.com/docker/go-connections v0.4.0
github.com/gofrs/uuid v4.4.0+incompatible github.com/gofrs/uuid v4.4.0+incompatible
github.com/sagernet/sing v0.2.1-0.20230318094614-4bbf5f2c3046 github.com/sagernet/sing v0.2.2-0.20230407053809-308e421e33c2
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9 github.com/sagernet/sing-shadowsocks v0.2.0
github.com/spyzhov/ajson v0.7.1 github.com/spyzhov/ajson v0.7.1
github.com/stretchr/testify v1.8.2 github.com/stretchr/testify v1.8.2
go.uber.org/goleak v1.2.0 go.uber.org/goleak v1.2.0
golang.org/x/net v0.8.0 golang.org/x/net v0.9.0
) )
require ( require (
berty.tech/go-libtor v1.0.385 // indirect berty.tech/go-libtor v1.0.385 // indirect
github.com/Dreamacro/clash v1.13.0 // indirect github.com/Dreamacro/clash v1.14.0 // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/ajg/form v1.5.1 // indirect github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect github.com/andybalholm/brotli v1.0.5 // indirect
@@ -42,14 +42,14 @@ require (
github.com/google/btree v1.0.1 // indirect github.com/google/btree v1.0.1 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect github.com/hashicorp/yamux v0.1.1 // indirect
github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961 // indirect github.com/insomniacslk/dhcp v0.0.0-20230327135226-74ae03f2425e // indirect
github.com/josharian/native v1.1.0 // indirect github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/compress v1.15.15 // indirect github.com/klauspost/compress v1.15.15 // indirect
github.com/klauspost/cpuid/v2 v2.1.1 // indirect github.com/klauspost/cpuid/v2 v2.1.1 // indirect
github.com/libdns/libdns v0.2.1 // indirect github.com/libdns/libdns v0.2.1 // indirect
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
github.com/mholt/acmez v1.1.0 // indirect github.com/mholt/acmez v1.1.0 // indirect
github.com/miekg/dns v1.1.52 // indirect github.com/miekg/dns v1.1.53 // indirect
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c // indirect github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c // indirect
github.com/morikuni/aec v1.0.0 // indirect github.com/morikuni/aec v1.0.0 // indirect
github.com/onsi/ginkgo/v2 v2.2.0 // indirect github.com/onsi/ginkgo/v2 v2.2.0 // indirect
@@ -69,10 +69,10 @@ require (
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 // indirect github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 // indirect
github.com/sagernet/reality v0.0.0-20230312150606-35ea9af0e0b8 // indirect github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
github.com/sagernet/sing-dns v0.1.4 // indirect github.com/sagernet/sing-dns v0.1.5-0.20230407055526-2a27418e7855 // indirect
github.com/sagernet/sing-shadowtls v0.1.0 // indirect github.com/sagernet/sing-shadowtls v0.1.0 // indirect
github.com/sagernet/sing-tun v0.1.3-0.20230315134716-fe89bbded22d // indirect github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab // indirect
github.com/sagernet/sing-vmess v0.1.3 // indirect github.com/sagernet/sing-vmess v0.1.3 // indirect
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 // indirect github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 // indirect
@@ -88,14 +88,14 @@ require (
go.uber.org/zap v1.24.0 // indirect go.uber.org/zap v1.24.0 // indirect
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35 // indirect go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35 // indirect
golang.org/x/crypto v0.7.0 // indirect golang.org/x/crypto v0.7.0 // indirect
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 // indirect golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
golang.org/x/mod v0.8.0 // indirect golang.org/x/mod v0.8.0 // indirect
golang.org/x/sys v0.6.0 // indirect golang.org/x/sys v0.7.0 // indirect
golang.org/x/text v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
golang.org/x/tools v0.6.0 // indirect golang.org/x/tools v0.6.0 // indirect
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
google.golang.org/grpc v1.53.0 // indirect google.golang.org/grpc v1.54.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.4.0 // indirect gotest.tools/v3 v3.4.0 // indirect

View File

@@ -1,8 +1,8 @@
berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw= berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw=
berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw= berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Dreamacro/clash v1.13.0 h1:gF0E0TluE1LCmuhhg0/bjqABYDmSnXkUjXjRhZxyrm8= github.com/Dreamacro/clash v1.14.0 h1:ehJ/C/1m9LEjmME72WSE/Y2YqbR3Q54AbjqiRCvtyW4=
github.com/Dreamacro/clash v1.13.0/go.mod h1:hf0RkWPsQ0e8oS8WVJBIRocY/1ILYzQQg9zeMwd8LsM= github.com/Dreamacro/clash v1.14.0/go.mod h1:ia2CU7V713H1QdCqMwOLK9U9V5Ay8X0voj3yQr2tk+I=
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
@@ -61,8 +61,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961 h1:x/YtdDlmypenG1te/FfH6LVM+3krhXk5CFV8VYNNX5M= github.com/insomniacslk/dhcp v0.0.0-20230327135226-74ae03f2425e h1:8ChxkWKTVYg7LKBvYNLNRnlobgbPrzzossZUoST2T7o=
github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI= github.com/insomniacslk/dhcp v0.0.0-20230327135226-74ae03f2425e/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI=
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
@@ -80,8 +80,8 @@ github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczG
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/mholt/acmez v1.1.0 h1:IQ9CGHKOHokorxnffsqDvmmE30mDenO1lptYZ1AYkHY= github.com/mholt/acmez v1.1.0 h1:IQ9CGHKOHokorxnffsqDvmmE30mDenO1lptYZ1AYkHY=
github.com/mholt/acmez v1.1.0/go.mod h1:zwo5+fbLLTowAX8o8ETfQzbDtwGEXnPhkmGdKIP+bgs= github.com/mholt/acmez v1.1.0/go.mod h1:zwo5+fbLLTowAX8o8ETfQzbDtwGEXnPhkmGdKIP+bgs=
github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c= github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw=
github.com/miekg/dns v1.1.52/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c h1:RC8WMpjonrBfyAh6VN/POIPtYD5tRAq0qMqCRjQNK+g= github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c h1:RC8WMpjonrBfyAh6VN/POIPtYD5tRAq0qMqCRjQNK+g=
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c/go.mod h1:9OcmHNQQUTbk4XCffrLgN1NEKc2mh5u++biHVrvHsSU= github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c/go.mod h1:9OcmHNQQUTbk4XCffrLgN1NEKc2mh5u++biHVrvHsSU=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
@@ -122,20 +122,20 @@ github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6E
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY= github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY=
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32/go.mod h1:QMCkxXAC3CvBgDZVIJp43NWTuwGBScCzMLVLynjERL8= github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32/go.mod h1:QMCkxXAC3CvBgDZVIJp43NWTuwGBScCzMLVLynjERL8=
github.com/sagernet/reality v0.0.0-20230312150606-35ea9af0e0b8 h1:4M3+0/kqvJuTsimEORH0/vUYTpMDUETBH2zNUU38SUw= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
github.com/sagernet/reality v0.0.0-20230312150606-35ea9af0e0b8/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
github.com/sagernet/sing v0.2.1-0.20230318094614-4bbf5f2c3046 h1:/+ZWbxRvQmco9ES2qT5Eh/x/IiQRjAcUyRG/vQ4dpxc= github.com/sagernet/sing v0.2.2-0.20230407053809-308e421e33c2 h1:VjeHDxEgpB2fqK5G16yBvtLacibvg3h2MsIjal0UXH0=
github.com/sagernet/sing v0.2.1-0.20230318094614-4bbf5f2c3046/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw= github.com/sagernet/sing v0.2.2-0.20230407053809-308e421e33c2/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw=
github.com/sagernet/sing-dns v0.1.4 h1:7VxgeoSCiiazDSaXXQVcvrTBxFpOePPq/4XdgnUDN+0= github.com/sagernet/sing-dns v0.1.5-0.20230407055526-2a27418e7855 h1:a3W2X1n5C/oYGp/Dd26eoymME3iXN8TJq7LZtO2MSUY=
github.com/sagernet/sing-dns v0.1.4/go.mod h1:1+6pCa48B1AI78lD+/i/dLgpw4MwfnsSpZo0Ds8wzzk= github.com/sagernet/sing-dns v0.1.5-0.20230407055526-2a27418e7855/go.mod h1:69PNSHyEmXdjf6C+bXBOdr2GQnPeEyWjIzo/MV8gmz8=
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9 h1:qS39eA4C7x+zhEkySbASrtmb6ebdy5v0y2M6mgkmSO0= github.com/sagernet/sing-shadowsocks v0.2.0 h1:ILDWL7pwWfkPLEbviE/MyCgfjaBmJY/JVVY+5jhSb58=
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9/go.mod h1:f3mHTy5shnVM9l8UocMlJgC/1G/zdj5FuEuVXhDinGU= github.com/sagernet/sing-shadowsocks v0.2.0/go.mod h1:ysYzszRLpNzJSorvlWRMuzU6Vchsp7sd52q+JNY4axw=
github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ= github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ=
github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc= github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc=
github.com/sagernet/sing-tun v0.1.3-0.20230315134716-fe89bbded22d h1:1gt4Hu2fHCrmL2NZYCNJ3nCgeczuhK09oCMni9mZmZk= github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab h1:a9oeWuPBuIZ70qMhIIH6RrYhp886xN9jJIwsuu4ZFUo=
github.com/sagernet/sing-tun v0.1.3-0.20230315134716-fe89bbded22d/go.mod h1:KnRkwaDHbb06zgeNPu0LQ8A+vA9myMxKEgHN1brCPHg= github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab/go.mod h1:4YxIDEkkCjGXDOTMPw1SXpLmCQUFAWuaQN250oo+928=
github.com/sagernet/sing-vmess v0.1.3 h1:q/+tsF46dvvapL6CpQBgPHJ6nQrDUZqEtLHCbsjO7iM= github.com/sagernet/sing-vmess v0.1.3 h1:q/+tsF46dvvapL6CpQBgPHJ6nQrDUZqEtLHCbsjO7iM=
github.com/sagernet/sing-vmess v0.1.3/go.mod h1:GVXqAHwe9U21uS+Voh4YBIrADQyE4F9v0ayGSixSQAE= github.com/sagernet/sing-vmess v0.1.3/go.mod h1:GVXqAHwe9U21uS+Voh4YBIrADQyE4F9v0ayGSixSQAE=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
@@ -191,8 +191,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 h1:pVgRXcIictcr+lBQIFeiwuwtDIs4eL21OuM9nyAADmo= golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -206,8 +206,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 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-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -232,15 +232,15 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -257,8 +257,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w=
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=

View File

@@ -35,6 +35,7 @@ func init() {
} }
type Transport struct { type Transport struct {
name string
ctx context.Context ctx context.Context
router adapter.Router router adapter.Router
logger logger.Logger logger logger.Logger
@@ -46,7 +47,7 @@ type Transport struct {
updatedAt time.Time updatedAt time.Time
} }
func NewTransport(ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) { func NewTransport(name string, ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
linkURL, err := url.Parse(link) linkURL, err := url.Parse(link)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -59,6 +60,7 @@ func NewTransport(ctx context.Context, logger logger.ContextLogger, dialer N.Dia
return nil, E.New("missing router in context") return nil, E.New("missing router in context")
} }
transport := &Transport{ transport := &Transport{
name: name,
ctx: ctx, ctx: ctx,
router: router, router: router,
logger: logger, logger: logger,
@@ -68,6 +70,10 @@ func NewTransport(ctx context.Context, logger logger.ContextLogger, dialer N.Dia
return transport, nil return transport, nil
} }
func (t *Transport) Name() string {
return t.name
}
func (t *Transport) Start() error { func (t *Transport) Start() error {
err := t.fetchServers() err := t.fetchServers()
if err != nil { if err != nil {
@@ -247,7 +253,7 @@ func (t *Transport) recreateServers(iface *net.Interface, serverAddrs []netip.Ad
}) })
var transports []dns.Transport var transports []dns.Transport
for _, serverAddr := range serverAddrs { for _, serverAddr := range serverAddrs {
serverTransport, err := dns.NewUDPTransport(t.ctx, serverDialer, M.Socksaddr{Addr: serverAddr, Port: 53}) serverTransport, err := dns.NewUDPTransport(t.name, t.ctx, serverDialer, M.Socksaddr{Addr: serverAddr, Port: 53})
if err != nil { if err != nil {
return err return err
} }

View File

@@ -498,7 +498,12 @@ func (c *PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
return return
} }
n = copy(p, msg.Data) n = copy(p, msg.Data)
addr = M.ParseSocksaddrHostPort(msg.Host, msg.Port).UDPAddr() destination := M.ParseSocksaddrHostPort(msg.Host, msg.Port)
if destination.IsFqdn() {
addr = destination
} else {
addr = destination.UDPAddr()
}
return return
} }

View File

@@ -32,10 +32,10 @@ type TLSObfs struct {
func (to *TLSObfs) read(b []byte, discardN int) (int, error) { func (to *TLSObfs) read(b []byte, discardN int) (int, error) {
buf := B.Get(discardN) buf := B.Get(discardN)
_, err := io.ReadFull(to.Conn, buf) _, err := io.ReadFull(to.Conn, buf)
B.Put(buf)
if err != nil { if err != nil {
return 0, err return 0, err
} }
B.Put(buf)
sizeBuf := make([]byte, 2) sizeBuf := make([]byte, 2)
_, err = io.ReadFull(to.Conn, sizeBuf) _, err = io.ReadFull(to.Conn, sizeBuf)

View File

@@ -66,8 +66,8 @@ func newV2RayPlugin(pluginOpts Args, router adapter.Router, dialer N.Dialer, ser
transportOptions = option.V2RayTransportOptions{ transportOptions = option.V2RayTransportOptions{
Type: C.V2RayTransportTypeWebsocket, Type: C.V2RayTransportTypeWebsocket,
WebsocketOptions: option.V2RayWebsocketOptions{ WebsocketOptions: option.V2RayWebsocketOptions{
Headers: map[string]string{ Headers: map[string]option.Listable[string]{
"Host": host, "Host": []string{host},
}, },
Path: path, Path: path,
}, },

View File

@@ -131,7 +131,11 @@ func (c *ClientPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error)
return return
} }
n = buffer.Len() n = buffer.Len()
addr = destination.UDPAddr() if destination.IsFqdn() {
addr = destination
} else {
addr = destination.UDPAddr()
}
return return
} }

View File

@@ -46,7 +46,7 @@ func NewClientTransport(ctx context.Context, dialer N.Dialer, serverAddr M.Socks
} }
switch options.Type { switch options.Type {
case C.V2RayTransportTypeHTTP: case C.V2RayTransportTypeHTTP:
return v2rayhttp.NewClient(ctx, dialer, serverAddr, options.HTTPOptions, tlsConfig), nil return v2rayhttp.NewClient(ctx, dialer, serverAddr, options.HTTPOptions, tlsConfig)
case C.V2RayTransportTypeGRPC: case C.V2RayTransportTypeGRPC:
if tlsConfig == nil { if tlsConfig == nil {
return nil, C.ErrTLSRequired return nil, C.ErrTLSRequired

View File

@@ -101,10 +101,10 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
return nil, err return nil, err
} }
client := NewGunServiceClient(clientConn).(GunServiceCustomNameClient) client := NewGunServiceClient(clientConn).(GunServiceCustomNameClient)
ctx, cancel := context.WithCancel(ctx) ctx, cancel := common.ContextWithCancelCause(ctx)
stream, err := client.TunCustomName(ctx, c.serviceName) stream, err := client.TunCustomName(ctx, c.serviceName)
if err != nil { if err != nil {
cancel() cancel(err)
return nil, err return nil, err
} }
return NewGRPCConn(stream, cancel), nil return NewGRPCConn(stream, cancel), nil

View File

@@ -1,12 +1,12 @@
package v2raygrpc package v2raygrpc
import ( import (
"context"
"net" "net"
"os" "os"
"time" "time"
"github.com/sagernet/sing-box/common/baderror" "github.com/sagernet/sing-box/common/baderror"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/rw"
) )
@@ -14,11 +14,11 @@ var _ net.Conn = (*GRPCConn)(nil)
type GRPCConn struct { type GRPCConn struct {
GunService GunService
cancel context.CancelFunc cancel common.ContextCancelCauseFunc
cache []byte cache []byte
} }
func NewGRPCConn(service GunService, cancel context.CancelFunc) *GRPCConn { func NewGRPCConn(service GunService, cancel common.ContextCancelCauseFunc) *GRPCConn {
if client, isClient := service.(GunService_TunClient); isClient { if client, isClient := service.(GunService_TunClient); isClient {
service = &clientConnWrapper{client} service = &clientConnWrapper{client}
} }
@@ -37,6 +37,7 @@ func (c *GRPCConn) Read(b []byte) (n int, err error) {
hunk, err := c.Recv() hunk, err := c.Recv()
err = baderror.WrapGRPC(err) err = baderror.WrapGRPC(err)
if err != nil { if err != nil {
c.cancel(err)
return return
} }
n = copy(b, hunk.Data) n = copy(b, hunk.Data)
@@ -49,13 +50,14 @@ func (c *GRPCConn) Read(b []byte) (n int, err error) {
func (c *GRPCConn) Write(b []byte) (n int, err error) { func (c *GRPCConn) Write(b []byte) (n int, err error) {
err = baderror.WrapGRPC(c.Send(&Hunk{Data: b})) err = baderror.WrapGRPC(c.Send(&Hunk{Data: b}))
if err != nil { if err != nil {
c.cancel(err)
return return
} }
return len(b), nil return len(b), nil
} }
func (c *GRPCConn) Close() error { func (c *GRPCConn) Close() error {
c.cancel() c.cancel(net.ErrClosed)
return nil return nil
} }

View File

@@ -10,9 +10,11 @@ import (
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/tls" "github.com/sagernet/sing-box/common/tls"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"golang.org/x/net/http2"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/keepalive" "google.golang.org/grpc/keepalive"
gM "google.golang.org/grpc/metadata" gM "google.golang.org/grpc/metadata"
@@ -30,7 +32,9 @@ type Server struct {
func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig tls.ServerConfig, handler N.TCPConnectionHandler) (*Server, error) { func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig tls.ServerConfig, handler N.TCPConnectionHandler) (*Server, error) {
var serverOptions []grpc.ServerOption var serverOptions []grpc.ServerOption
if tlsConfig != nil { if tlsConfig != nil {
tlsConfig.SetNextProtos([]string{"h2"}) if !common.Contains(tlsConfig.NextProtos(), http2.NextProtoTLS) {
tlsConfig.SetNextProtos(append([]string{"h2"}, tlsConfig.NextProtos()...))
}
serverOptions = append(serverOptions, grpc.Creds(NewTLSTransportCredentials(tlsConfig))) serverOptions = append(serverOptions, grpc.Creds(NewTLSTransportCredentials(tlsConfig)))
} }
if options.IdleTimeout > 0 { if options.IdleTimeout > 0 {
@@ -45,7 +49,7 @@ func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig t
} }
func (s *Server) Tun(server GunService_TunServer) error { func (s *Server) Tun(server GunService_TunServer) error {
ctx, cancel := context.WithCancel(s.ctx) ctx, cancel := common.ContextWithCancelCause(s.ctx)
conn := NewGRPCConn(server, cancel) conn := NewGRPCConn(server, cancel)
var metadata M.Metadata var metadata M.Metadata
if remotePeer, loaded := peer.FromContext(server.Context()); loaded { if remotePeer, loaded := peer.FromContext(server.Context()); loaded {

View File

@@ -92,14 +92,16 @@ func (s *Server) fallbackRequest(ctx context.Context, writer http.ResponseWriter
} else if fErr == os.ErrInvalid { } else if fErr == os.ErrInvalid {
fErr = nil fErr = nil
} }
writer.WriteHeader(statusCode) if statusCode > 0 {
writer.WriteHeader(statusCode)
}
s.handler.NewError(request.Context(), E.Cause(E.Errors(err, E.Cause(fErr, "fallback connection")), "process connection from ", request.RemoteAddr)) s.handler.NewError(request.Context(), E.Cause(E.Errors(err, E.Cause(fErr, "fallback connection")), "process connection from ", request.RemoteAddr))
} }
func (s *Server) Serve(listener net.Listener) error { func (s *Server) Serve(listener net.Listener) error {
if s.tlsConfig != nil { if s.tlsConfig != nil {
if len(s.tlsConfig.NextProtos()) == 0 { if !common.Contains(s.tlsConfig.NextProtos(), http2.NextProtoTLS) {
s.tlsConfig.SetNextProtos([]string{http2.NextProtoTLS}) s.tlsConfig.SetNextProtos(append([]string{"h2"}, s.tlsConfig.NextProtos()...))
} }
listener = aTLS.NewListener(listener, s.tlsConfig) listener = aTLS.NewListener(listener, s.tlsConfig)
} }

View File

@@ -1,14 +1,12 @@
package v2rayhttp package v2rayhttp
import ( import (
"bufio"
"context" "context"
"io" "io"
"math/rand" "math/rand"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
"strings"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@@ -17,6 +15,7 @@ import (
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
sHTTP "github.com/sagernet/sing/protocol/http"
"golang.org/x/net/http2" "golang.org/x/net/http2"
) )
@@ -35,7 +34,7 @@ type Client struct {
headers http.Header headers http.Header
} }
func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayHTTPOptions, tlsConfig tls.Config) adapter.V2RayClientTransport { func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayHTTPOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) {
var transport http.RoundTripper var transport http.RoundTripper
if tlsConfig == nil { if tlsConfig == nil {
transport = &http.Transport{ transport = &http.Transport{
@@ -78,14 +77,15 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
} }
uri.Host = serverAddr.String() uri.Host = serverAddr.String()
uri.Path = options.Path uri.Path = options.Path
if !strings.HasPrefix(uri.Path, "/") { err := sHTTP.URLSetPath(&uri, options.Path)
uri.Path = "/" + uri.Path if err != nil {
return nil, E.New("failed to set path: " + err.Error())
} }
for key, value := range options.Headers { for key, valueList := range options.Headers {
client.headers.Set(key, value) client.headers[key] = valueList
} }
client.url = &uri client.url = &uri
return client return client, nil
} }
func (c *Client) DialContext(ctx context.Context) (net.Conn, error) { func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
@@ -97,18 +97,16 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
} }
func (c *Client) dialHTTP(ctx context.Context) (net.Conn, error) { func (c *Client) dialHTTP(ctx context.Context) (net.Conn, error) {
conn, err := c.dialer.DialContext(c.ctx, N.NetworkTCP, c.serverAddr) conn, err := c.dialer.DialContext(ctx, N.NetworkTCP, c.serverAddr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
request := &http.Request{ request := &http.Request{
Method: c.method, Method: c.method,
URL: c.url, URL: c.url,
ProtoMajor: 1, Header: c.headers.Clone(),
Proto: "HTTP/1.1",
Header: c.headers.Clone(),
} }
request = request.WithContext(ctx)
switch hostLen := len(c.host); hostLen { switch hostLen := len(c.host); hostLen {
case 0: case 0:
request.Host = c.serverAddr.AddrString() request.Host = c.serverAddr.AddrString()
@@ -117,30 +115,17 @@ func (c *Client) dialHTTP(ctx context.Context) (net.Conn, error) {
default: default:
request.Host = c.host[rand.Intn(hostLen)] request.Host = c.host[rand.Intn(hostLen)]
} }
err = request.Write(conn)
if err != nil { return NewHTTP1Conn(conn, request), nil
return nil, err
}
reader := bufio.NewReader(conn)
response, err := http.ReadResponse(reader, request)
if err != nil {
return nil, err
}
if response.StatusCode != 200 {
return nil, E.New("unexpected status: ", response.Status)
}
return conn, nil
} }
func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) { func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) {
pipeInReader, pipeInWriter := io.Pipe() pipeInReader, pipeInWriter := io.Pipe()
request := &http.Request{ request := &http.Request{
Method: c.method, Method: c.method,
Body: pipeInReader, Body: pipeInReader,
URL: c.url, URL: c.url,
ProtoMajor: 2, Header: c.headers.Clone(),
Proto: "HTTP/2",
Header: c.headers.Clone(),
} }
request = request.WithContext(ctx) request = request.WithContext(ctx)
switch hostLen := len(c.host); hostLen { switch hostLen := len(c.host); hostLen {
@@ -152,8 +137,6 @@ func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) {
default: default:
request.Host = c.host[rand.Intn(hostLen)] request.Host = c.host[rand.Intn(hostLen)]
} }
// Disable any compression method from server.
request.Header.Set("Accept-Encoding", "identity")
conn := newLateHTTPConn(pipeInWriter) conn := newLateHTTPConn(pipeInWriter)
go func() { go func() {
response, err := c.transport.RoundTrip(request) response, err := c.transport.RoundTrip(request)

View File

@@ -1,10 +1,12 @@
package v2rayhttp package v2rayhttp
import ( import (
std_bufio "bufio"
"io" "io"
"net" "net"
"net/http" "net/http"
"os" "os"
"strings"
"sync" "sync"
"time" "time"
@@ -12,37 +14,146 @@ import (
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
) )
type HTTPConn struct { type HTTPConn struct {
net.Conn
request *http.Request
requestWritten bool
responseRead bool
responseCache *buf.Buffer
}
func NewHTTP1Conn(conn net.Conn, request *http.Request) *HTTPConn {
return &HTTPConn{
Conn: conn,
request: request,
}
}
func (c *HTTPConn) Read(b []byte) (n int, err error) {
if !c.responseRead {
reader := std_bufio.NewReader(c.Conn)
response, err := http.ReadResponse(reader, c.request)
if err != nil {
return 0, E.Cause(err, "read response")
}
if response.StatusCode != 200 {
return 0, E.New("unexpected status: ", response.Status)
}
if cacheLen := reader.Buffered(); cacheLen > 0 {
c.responseCache = buf.NewSize(cacheLen)
_, err = c.responseCache.ReadFullFrom(reader, cacheLen)
if err != nil {
c.responseCache.Release()
return 0, E.Cause(err, "read cache")
}
}
c.responseRead = true
}
if c.responseCache != nil {
n, err = c.responseCache.Read(b)
if err == io.EOF {
c.responseCache.Release()
c.responseCache = nil
}
if n > 0 {
return n, nil
}
}
return c.Conn.Read(b)
}
func (c *HTTPConn) Write(b []byte) (int, error) {
if !c.requestWritten {
err := c.writeRequest(b)
if err != nil {
return 0, E.Cause(err, "write request")
}
c.requestWritten = true
return len(b), nil
}
return c.Conn.Write(b)
}
func (c *HTTPConn) writeRequest(payload []byte) error {
writer := bufio.NewBufferedWriter(c.Conn, buf.New())
const CRLF = "\r\n"
_, err := writer.Write([]byte(F.ToString(c.request.Method, " ", c.request.URL.RequestURI(), " HTTP/1.1", CRLF)))
if err != nil {
return err
}
if c.request.Header.Get("Host") == "" {
c.request.Header.Set("Host", c.request.Host)
}
for key, value := range c.request.Header {
_, err = writer.Write([]byte(F.ToString(key, ": ", strings.Join(value, ", "), CRLF)))
if err != nil {
return err
}
}
_, err = writer.Write([]byte(CRLF))
if err != nil {
return err
}
_, err = writer.Write(payload)
if err != nil {
return err
}
err = writer.Fallthrough()
if err != nil {
return err
}
return nil
}
func (c *HTTPConn) ReaderReplaceable() bool {
return c.responseRead
}
func (c *HTTPConn) WriterReplaceable() bool {
return c.requestWritten
}
func (c *HTTPConn) NeedHandshake() bool {
return !c.requestWritten
}
func (c *HTTPConn) Upstream() any {
return c.Conn
}
type HTTP2Conn struct {
reader io.Reader reader io.Reader
writer io.Writer writer io.Writer
create chan struct{} create chan struct{}
err error err error
} }
func NewHTTPConn(reader io.Reader, writer io.Writer) HTTPConn { func NewHTTPConn(reader io.Reader, writer io.Writer) HTTP2Conn {
return HTTPConn{ return HTTP2Conn{
reader: reader, reader: reader,
writer: writer, writer: writer,
} }
} }
func newLateHTTPConn(writer io.Writer) *HTTPConn { func newLateHTTPConn(writer io.Writer) *HTTP2Conn {
return &HTTPConn{ return &HTTP2Conn{
create: make(chan struct{}), create: make(chan struct{}),
writer: writer, writer: writer,
} }
} }
func (c *HTTPConn) setup(reader io.Reader, err error) { func (c *HTTP2Conn) setup(reader io.Reader, err error) {
c.reader = reader c.reader = reader
c.err = err c.err = err
close(c.create) close(c.create)
} }
func (c *HTTPConn) Read(b []byte) (n int, err error) { func (c *HTTP2Conn) Read(b []byte) (n int, err error) {
if c.reader == nil { if c.reader == nil {
<-c.create <-c.create
if c.err != nil { if c.err != nil {
@@ -53,24 +164,24 @@ func (c *HTTPConn) Read(b []byte) (n int, err error) {
return n, baderror.WrapH2(err) return n, baderror.WrapH2(err)
} }
func (c *HTTPConn) Write(b []byte) (n int, err error) { func (c *HTTP2Conn) Write(b []byte) (n int, err error) {
n, err = c.writer.Write(b) n, err = c.writer.Write(b)
return n, baderror.WrapH2(err) return n, baderror.WrapH2(err)
} }
func (c *HTTPConn) Close() error { func (c *HTTP2Conn) Close() error {
return common.Close(c.reader, c.writer) return common.Close(c.reader, c.writer)
} }
func (c *HTTPConn) LocalAddr() net.Addr { func (c *HTTP2Conn) LocalAddr() net.Addr {
return nil return nil
} }
func (c *HTTPConn) RemoteAddr() net.Addr { func (c *HTTP2Conn) RemoteAddr() net.Addr {
return nil return nil
} }
func (c *HTTPConn) SetDeadline(t time.Time) error { func (c *HTTP2Conn) SetDeadline(t time.Time) error {
if responseWriter, loaded := c.writer.(interface { if responseWriter, loaded := c.writer.(interface {
SetWriteDeadline(time.Time) error SetWriteDeadline(time.Time) error
}); loaded { }); loaded {
@@ -79,7 +190,7 @@ func (c *HTTPConn) SetDeadline(t time.Time) error {
return os.ErrInvalid return os.ErrInvalid
} }
func (c *HTTPConn) SetReadDeadline(t time.Time) error { func (c *HTTP2Conn) SetReadDeadline(t time.Time) error {
if responseWriter, loaded := c.writer.(interface { if responseWriter, loaded := c.writer.(interface {
SetReadDeadline(time.Time) error SetReadDeadline(time.Time) error
}); loaded { }); loaded {
@@ -88,7 +199,7 @@ func (c *HTTPConn) SetReadDeadline(t time.Time) error {
return os.ErrInvalid return os.ErrInvalid
} }
func (c *HTTPConn) SetWriteDeadline(t time.Time) error { func (c *HTTP2Conn) SetWriteDeadline(t time.Time) error {
if responseWriter, loaded := c.writer.(interface { if responseWriter, loaded := c.writer.(interface {
SetWriteDeadline(time.Time) error SetWriteDeadline(time.Time) error
}); loaded { }); loaded {
@@ -98,7 +209,7 @@ func (c *HTTPConn) SetWriteDeadline(t time.Time) error {
} }
type ServerHTTPConn struct { type ServerHTTPConn struct {
HTTPConn HTTP2Conn
flusher http.Flusher flusher http.Flusher
} }

View File

@@ -13,6 +13,8 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
@@ -62,7 +64,7 @@ func NewServer(ctx context.Context, options option.V2RayHTTPOptions, tlsConfig t
server.path = "/" + server.path server.path = "/" + server.path
} }
for key, value := range options.Headers { for key, value := range options.Headers {
server.headers.Set(key, value) server.headers[key] = value
} }
server.httpServer = &http.Server{ server.httpServer = &http.Server{
Handler: server, Handler: server,
@@ -100,19 +102,40 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
} }
} }
writer.WriteHeader(http.StatusOK)
writer.(http.Flusher).Flush()
var metadata M.Metadata var metadata M.Metadata
metadata.Source = sHttp.SourceAddress(request) metadata.Source = sHttp.SourceAddress(request)
if h, ok := writer.(http.Hijacker); ok { if h, ok := writer.(http.Hijacker); ok {
conn, _, err := h.Hijack() var requestBody *buf.Buffer
if contentLength := int(request.ContentLength); contentLength > 0 {
requestBody = buf.NewSize(contentLength)
_, err := requestBody.ReadFullFrom(request.Body, contentLength)
if err != nil {
s.fallbackRequest(request.Context(), writer, request, 0, E.Cause(err, "read request"))
return
}
}
writer.WriteHeader(http.StatusOK)
writer.(http.Flusher).Flush()
conn, reader, err := h.Hijack()
if err != nil { if err != nil {
s.fallbackRequest(request.Context(), writer, request, http.StatusInternalServerError, E.Cause(err, "hijack conn")) s.fallbackRequest(request.Context(), writer, request, 0, E.Cause(err, "hijack conn"))
return return
} }
if cacheLen := reader.Reader.Buffered(); cacheLen > 0 {
cache := buf.NewSize(cacheLen)
_, err = cache.ReadFullFrom(reader.Reader, cacheLen)
if err != nil {
s.fallbackRequest(request.Context(), writer, request, 0, E.Cause(err, "read cache"))
return
}
conn = bufio.NewCachedConn(conn, cache)
}
if requestBody != nil {
conn = bufio.NewCachedConn(conn, requestBody)
}
s.handler.NewConnection(request.Context(), conn, metadata) s.handler.NewConnection(request.Context(), conn, metadata)
} else { } else {
writer.WriteHeader(http.StatusOK)
conn := NewHTTP2Wrapper(&ServerHTTPConn{ conn := NewHTTP2Wrapper(&ServerHTTPConn{
NewHTTPConn(request.Body, writer), NewHTTPConn(request.Body, writer),
writer.(http.Flusher), writer.(http.Flusher),
@@ -130,12 +153,19 @@ func (s *Server) fallbackRequest(ctx context.Context, writer http.ResponseWriter
} else if fErr == os.ErrInvalid { } else if fErr == os.ErrInvalid {
fErr = nil fErr = nil
} }
writer.WriteHeader(statusCode) if statusCode > 0 {
writer.WriteHeader(statusCode)
}
s.handler.NewError(request.Context(), E.Cause(E.Errors(err, E.Cause(fErr, "fallback connection")), "process connection from ", request.RemoteAddr)) s.handler.NewError(request.Context(), E.Cause(E.Errors(err, E.Cause(fErr, "fallback connection")), "process connection from ", request.RemoteAddr))
} }
func (s *Server) Serve(listener net.Listener) error { func (s *Server) Serve(listener net.Listener) error {
if s.tlsConfig != nil { if s.tlsConfig != nil {
if len(s.tlsConfig.NextProtos()) == 0 {
s.tlsConfig.SetNextProtos([]string{http2.NextProtoTLS, "http/1.1"})
} else if !common.Contains(s.tlsConfig.NextProtos(), http2.NextProtoTLS) {
s.tlsConfig.SetNextProtos(append([]string{"h2"}, s.tlsConfig.NextProtos()...))
}
listener = aTLS.NewListener(listener, s.tlsConfig) listener = aTLS.NewListener(listener, s.tlsConfig)
} }
return s.httpServer.Serve(listener) return s.httpServer.Serve(listener)

View File

@@ -5,7 +5,6 @@ import (
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
"strings"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@@ -14,6 +13,7 @@ import (
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
sHTTP "github.com/sagernet/sing/protocol/http"
"github.com/sagernet/websocket" "github.com/sagernet/websocket"
) )
@@ -57,12 +57,13 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
} }
uri.Host = serverAddr.String() uri.Host = serverAddr.String()
uri.Path = options.Path uri.Path = options.Path
if !strings.HasPrefix(uri.Path, "/") { err := sHTTP.URLSetPath(&uri, options.Path)
uri.Path = "/" + uri.Path if err != nil {
return nil
} }
headers := make(http.Header) headers := make(http.Header)
for key, value := range options.Headers { for key, value := range options.Headers {
headers.Set(key, value) headers[key] = value
} }
return &Client{ return &Client{
wsDialer, wsDialer,

View File

@@ -69,6 +69,14 @@ func (c *WebsocketConn) SetDeadline(t time.Time) error {
return os.ErrInvalid return os.ErrInvalid
} }
func (c *WebsocketConn) SetReadDeadline(t time.Time) error {
return os.ErrInvalid
}
func (c *WebsocketConn) SetWriteDeadline(t time.Time) error {
return os.ErrInvalid
}
func (c *WebsocketConn) Upstream() any { func (c *WebsocketConn) Upstream() any {
return c.Conn.NetConn() return c.Conn.NetConn()
} }
@@ -195,24 +203,15 @@ func (c *EarlyWebsocketConn) RemoteAddr() net.Addr {
} }
func (c *EarlyWebsocketConn) SetDeadline(t time.Time) error { func (c *EarlyWebsocketConn) SetDeadline(t time.Time) error {
if c.conn == nil { return os.ErrInvalid
return os.ErrInvalid
}
return c.conn.SetDeadline(t)
} }
func (c *EarlyWebsocketConn) SetReadDeadline(t time.Time) error { func (c *EarlyWebsocketConn) SetReadDeadline(t time.Time) error {
if c.conn == nil { return os.ErrInvalid
return os.ErrInvalid
}
return c.conn.SetReadDeadline(t)
} }
func (c *EarlyWebsocketConn) SetWriteDeadline(t time.Time) error { func (c *EarlyWebsocketConn) SetWriteDeadline(t time.Time) error {
if c.conn == nil { return os.ErrInvalid
return os.ErrInvalid
}
return c.conn.SetWriteDeadline(t)
} }
func (c *EarlyWebsocketConn) Upstream() any { func (c *EarlyWebsocketConn) Upstream() any {

View File

@@ -95,7 +95,7 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
} }
wsConn, err := upgrader.Upgrade(writer, request, nil) wsConn, err := upgrader.Upgrade(writer, request, nil)
if err != nil { if err != nil {
s.fallbackRequest(request.Context(), writer, request, http.StatusBadRequest, E.Cause(err, "upgrade websocket connection")) s.fallbackRequest(request.Context(), writer, request, 0, E.Cause(err, "upgrade websocket connection"))
return return
} }
var metadata M.Metadata var metadata M.Metadata
@@ -115,7 +115,9 @@ func (s *Server) fallbackRequest(ctx context.Context, writer http.ResponseWriter
} else if fErr == os.ErrInvalid { } else if fErr == os.ErrInvalid {
fErr = nil fErr = nil
} }
writer.WriteHeader(statusCode) if statusCode > 0 {
writer.WriteHeader(statusCode)
}
s.handler.NewError(request.Context(), E.Cause(E.Errors(err, E.Cause(fErr, "fallback connection")), "process connection from ", request.RemoteAddr)) s.handler.NewError(request.Context(), E.Cause(E.Errors(err, E.Cause(fErr, "fallback connection")), "process connection from ", request.RemoteAddr))
} }

View File

@@ -196,7 +196,11 @@ func (c *PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
if err != nil { if err != nil {
return return
} }
addr = c.destination.UDPAddr() if c.destination.IsFqdn() {
addr = c.destination
} else {
addr = c.destination.UDPAddr()
}
return return
} }

View File

@@ -146,7 +146,11 @@ func (c *serverPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error)
if err != nil { if err != nil {
return return
} }
addr = c.destination.UDPAddr() if c.destination.IsFqdn() {
addr = c.destination
} else {
addr = c.destination.UDPAddr()
}
return return
} }