Compare commits

..

56 Commits

Author SHA1 Message Date
世界
420d8d144a Add tls fragment support 2025-01-26 19:06:27 +08:00
世界
7ecc62f2e0 Fix local server 2025-01-26 09:00:43 +08:00
世界
3fa417f283 Add hosts transport & Remove bad linkname usages 2025-01-25 19:37:11 +08:00
世界
314b25bccf Fix https DNS transports 2025-01-25 19:03:53 +08:00
世界
1c56213cbe Add ip_accept_any DNS rule action 2025-01-25 19:02:41 +08:00
世界
1947a90878 Add dns.rules[].strategy 2025-01-25 19:01:53 +08:00
世界
63ef6f972f refactor: Outbound domain resolver 2025-01-19 16:04:21 +08:00
世界
eae2631208 refactor: DNS 2025-01-19 15:57:44 +08:00
世界
8cc7734a92 documentation: Bump version 2025-01-19 09:54:44 +08:00
世界
d74a05fdff Fix legacy routes 2025-01-19 09:40:09 +08:00
HystericalDragon
4f453e81ac Fix endpoints not close 2025-01-17 11:21:07 +08:00
世界
bd2e052815 documentation: Bump version 2025-01-13 15:14:30 +08:00
世界
e65926fd08 Fix tests 2025-01-13 15:14:30 +08:00
世界
f2ec319fe1 Fix system time 2025-01-13 15:14:30 +08:00
世界
32377a61b7 Add port hopping for hysteria2 2025-01-13 15:14:30 +08:00
世界
7aac801ccd tun: Set address sets to routes 2025-01-13 15:14:30 +08:00
世界
96fdf59ee4 Fix default dialer on legacy xiaomi systems 2025-01-13 15:14:30 +08:00
世界
50b8f3ab94 Add rule-set merge command 2025-01-13 15:14:30 +08:00
世界
ff7aaf977b Fix DNS match 2025-01-13 15:14:30 +08:00
世界
9a1efbe54d Fix domain strategy 2025-01-13 15:14:30 +08:00
世界
906c21f458 Fix time service 2025-01-13 15:14:30 +08:00
世界
d5e7af7a7e Fix socks5 UDP implementation 2025-01-13 15:14:30 +08:00
世界
4d41f03bd5 clash-api: Fix missing endpoints 2025-01-13 15:14:30 +08:00
世界
30704a15a7 hysteria2: Add more masquerade options 2025-01-13 15:14:30 +08:00
世界
83889178ed Improve timeouts 2025-01-13 15:14:30 +08:00
世界
1d2720bf5e Add UDP timeout route option 2025-01-13 15:14:30 +08:00
世界
c4b6d0eadb Make GSO adaptive 2025-01-13 15:14:30 +08:00
世界
0c66888691 Fix lint 2025-01-13 15:14:30 +08:00
世界
68781387fe refactor: WireGuard endpoint 2025-01-13 15:14:30 +08:00
世界
fd299a0961 refactor: connection manager 2025-01-13 15:14:30 +08:00
世界
285a82050c documentation: Fix typo 2025-01-13 15:14:30 +08:00
世界
2dbb8c55c9 Add override destination to route options 2025-01-13 15:14:30 +08:00
世界
effcf39469 Add dns.cache_capacity 2025-01-13 15:14:30 +08:00
世界
9db9484863 Refactor multi networks strategy 2025-01-13 15:14:30 +08:00
世界
ca813f461b documentation: Remove unused titles 2025-01-13 15:14:30 +08:00
世界
bb46cdb2b3 Add multi network dialing 2025-01-13 15:14:30 +08:00
世界
dcb10c21a1 documentation: Merge route options to route actions 2025-01-13 15:14:29 +08:00
世界
05ea0ca00e Add network_[type/is_expensive/is_constrained] rule items 2025-01-13 15:14:29 +08:00
世界
c098f282b1 Merge route options to route actions 2025-01-13 15:14:29 +08:00
世界
ecf82d197c refactor: Platform Interfaces 2025-01-13 15:14:29 +08:00
世界
9afe75586a refactor: Extract services form router 2025-01-13 15:14:29 +08:00
世界
a1be455202 refactor: Modular network manager 2025-01-13 15:14:29 +08:00
世界
19fb214226 refactor: Modular inbound/outbound manager 2025-01-13 15:14:29 +08:00
世界
28ec898a8c documentation: Add rule action 2025-01-13 15:14:29 +08:00
世界
467b1bbeeb documentation: Update the scheduled removal time of deprecated features 2025-01-13 15:14:29 +08:00
世界
02ab8ce806 documentation: Remove outdated icons 2025-01-13 15:14:29 +08:00
世界
ce69e620e9 Migrate bad options to library 2025-01-13 15:14:29 +08:00
世界
1133cf3ef5 Implement udp connect 2025-01-13 15:14:29 +08:00
世界
59a607e303 Implement new deprecated warnings 2025-01-13 15:14:29 +08:00
世界
313be3d7a4 Improve rule actions 2025-01-13 15:14:29 +08:00
世界
4fe40fcee0 Remove unused reject methods 2025-01-13 15:14:29 +08:00
世界
e233fd4fe5 refactor: Modular inbounds/outbounds 2025-01-13 15:14:29 +08:00
世界
9f7683818f Implement dns-hijack 2025-01-13 15:14:29 +08:00
世界
179e3cb2f5 Implement resolve(server) 2025-01-13 15:14:29 +08:00
世界
41b960552d Implement TCP and ICMP rejects 2025-01-13 15:14:29 +08:00
世界
8304295c48 Crazy sekai overturns the small pond 2025-01-13 15:14:29 +08:00
57 changed files with 16999 additions and 1139 deletions

View File

@@ -32,7 +32,6 @@ run:
- with_reality_server
- with_acme
- with_clash_api
- badlinkname
issues:
exclude-dirs:

View File

@@ -9,7 +9,6 @@ builds:
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }}
- -s
- -buildid=
- -checklinkname=0
tags:
- with_gvisor
- with_quic

View File

@@ -11,7 +11,6 @@ builds:
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }}
- -s
- -buildid=
- -checklinkname=0
tags:
- with_gvisor
- with_quic

View File

@@ -15,7 +15,7 @@ RUN set -ex \
&& go build -v -trimpath -tags \
"with_gvisor,with_quic,with_dhcp,with_wireguard,with_ech,with_utls,with_reality_server,with_acme,with_clash_api" \
-o /go/bin/sing-box \
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=$VERSION\" -s -w -buildid= -checklinkname=0" \
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=$VERSION\" -s -w -buildid=" \
./cmd/sing-box
FROM --platform=$TARGETPLATFORM alpine AS dist
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"

View File

@@ -2,15 +2,14 @@ NAME = sing-box
COMMIT = $(shell git rev-parse --short HEAD)
TAGS_GO120 = with_gvisor,with_dhcp,with_wireguard,with_reality_server,with_clash_api,with_quic,with_utls
TAGS_GO121 = with_ech
TAGS_GO123 = with_tailscale,badlinkname
TAGS ?= $(TAGS_GO118),$(TAGS_GO120),$(TAGS_GO121),$(TAGS_GO123)
TAGS ?= $(TAGS_GO118),$(TAGS_GO120),$(TAGS_GO121)
TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_reality_server
GOHOSTOS = $(shell go env GOHOSTOS)
GOHOSTARCH = $(shell go env GOHOSTARCH)
VERSION=$(shell CGO_ENABLED=0 GOOS=$(GOHOSTOS) GOARCH=$(GOHOSTARCH) go run ./cmd/internal/read_tag)
PARAMS = -v -trimpath -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=$(VERSION)' -s -w -buildid= -checklinkname=0"
PARAMS = -v -trimpath -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=$(VERSION)' -s -w -buildid="
MAIN_PARAMS = $(PARAMS) -tags $(TAGS)
MAIN = ./cmd/sing-box
PREFIX ?= $(shell go env GOPATH)

View File

@@ -71,6 +71,8 @@ type InboundContext struct {
UDPDisableDomainUnmapping bool
UDPConnect bool
UDPTimeout time.Duration
TLSFragment bool
TLSFragmentFallbackDelay time.Duration
NetworkStrategy *C.NetworkStrategy
NetworkType []C.InterfaceType

2
box.go
View File

@@ -446,7 +446,7 @@ func (s *Box) Close() error {
close(s.done)
}
err := common.Close(
s.inbound, s.outbound, s.router, s.connection, s.dnsRouter, s.dnsTransport, s.network,
s.inbound, s.outbound, s.endpoint, s.router, s.connection, s.dnsRouter, s.dnsTransport, s.network,
)
for _, lifecycleService := range s.services {
err = E.Append(err, lifecycleService.Close(), func(err error) error {

View File

@@ -55,10 +55,10 @@ func init() {
if err != nil {
currentTag = "unknown"
}
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid= -checklinkname=0")
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid=")
debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag)
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_ech", "with_utls", "with_clash_api", "with_tailscale")
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_ech", "with_utls", "with_clash_api")
iosTags = append(iosTags, "with_dhcp", "with_low_memory", "with_conntrack")
debugTags = append(debugTags, "debug")
}

125
common/tlsfragment/conn.go Normal file
View File

@@ -0,0 +1,125 @@
package tf
import (
"context"
"math/rand"
"net"
"strings"
"syscall"
"time"
"github.com/sagernet/sing/common"
N "github.com/sagernet/sing/common/network"
)
type Conn struct {
net.Conn
syscallConn syscall.Conn
ctx context.Context
firstPacketWritten bool
fallbackDelay time.Duration
}
func NewConn(conn net.Conn, ctx context.Context, fallbackDelay time.Duration) (*Conn, error) {
syscallConn, _ := N.UnwrapReader(conn).(syscall.Conn)
return &Conn{
Conn: conn,
syscallConn: syscallConn,
ctx: ctx,
fallbackDelay: fallbackDelay,
}, nil
}
func (c *Conn) Write(b []byte) (n int, err error) {
if !c.firstPacketWritten {
defer func() {
c.firstPacketWritten = true
}()
serverName := indexTLSServerName(b)
if serverName != nil {
tcpConn, isTCPConn := c.syscallConn.(interface {
SetNoDelay(bool) error
})
if isTCPConn {
err = tcpConn.SetNoDelay(true)
if err != nil {
return
}
}
splits := strings.Split(string(b[serverName.Index:serverName.Index+serverName.Length]), ".")
currentIndex := serverName.Index
var striped bool
if len(splits) > 3 {
suffix := splits[len(splits)-3] + "." + splits[len(splits)-2] + "." + splits[len(splits)-1]
if publicSuffixMatcher().Match(suffix) {
splits = splits[:len(splits)-3]
}
striped = true
}
if !striped && len(splits) > 2 {
suffix := splits[len(splits)-2] + "." + splits[len(splits)-1]
if publicSuffixMatcher().Match(suffix) {
splits = splits[:len(splits)-2]
}
striped = true
}
if !striped && len(splits) > 1 {
suffix := splits[len(splits)-1]
if publicSuffixMatcher().Match(suffix) {
splits = splits[:len(splits)-1]
}
}
if len(splits) > 1 && common.Contains(publicPrefix, splits[0]) {
currentIndex += len(splits[0]) + 1
splits = splits[1:]
}
var splitIndexes []int
for i, split := range splits {
splitAt := rand.Intn(len(split))
splitIndexes = append(splitIndexes, currentIndex+splitAt)
currentIndex += len(split)
if i != len(splits)-1 {
currentIndex++
}
}
for i := 0; i <= len(splitIndexes); i++ {
if i == 0 {
_, err = c.Conn.Write(b[:splitIndexes[i]])
} else if i == len(splitIndexes) {
_, err = c.Conn.Write(b[splitIndexes[i-1]:])
} else {
_, err = c.Conn.Write(b[splitIndexes[i-1]:splitIndexes[i]])
}
if err != nil {
return
}
if c.syscallConn != nil && i != len(splitIndexes) {
err = waitAck(c.ctx, c.syscallConn, c.fallbackDelay)
if err != nil {
return
}
}
}
if isTCPConn {
err = tcpConn.SetNoDelay(false)
if err != nil {
return
}
}
return len(b), nil
}
}
return c.Conn.Write(b)
}
func (c *Conn) ReaderReplaceable() bool {
return true
}
func (c *Conn) WriterReplaceable() bool {
return c.firstPacketWritten
}
func (c *Conn) Upstream() any {
return c.Conn
}

137
common/tlsfragment/index.go Normal file
View File

@@ -0,0 +1,137 @@
package tf
import (
"encoding/binary"
)
const (
recordLayerHeaderLen int = 5
handshakeHeaderLen int = 6
randomDataLen int = 32
sessionIDHeaderLen int = 1
cipherSuiteHeaderLen int = 2
compressMethodHeaderLen int = 1
extensionsHeaderLen int = 2
extensionHeaderLen int = 4
sniExtensionHeaderLen int = 5
contentType uint8 = 22
handshakeType uint8 = 1
sniExtensionType uint16 = 0
sniNameDNSHostnameType uint8 = 0
tlsVersionBitmask uint16 = 0xFFFC
tls13 uint16 = 0x0304
)
type myServerName struct {
Index int
Length int
sex []byte
}
func indexTLSServerName(payload []byte) *myServerName {
if len(payload) < recordLayerHeaderLen || payload[0] != contentType {
return nil
}
segmentLen := binary.BigEndian.Uint16(payload[3:5])
if len(payload) < recordLayerHeaderLen+int(segmentLen) {
return nil
}
serverName := indexTLSServerNameFromHandshake(payload[recordLayerHeaderLen : recordLayerHeaderLen+int(segmentLen)])
if serverName == nil {
return nil
}
serverName.Length += recordLayerHeaderLen
return serverName
}
func indexTLSServerNameFromHandshake(hs []byte) *myServerName {
if len(hs) < handshakeHeaderLen+randomDataLen+sessionIDHeaderLen {
return nil
}
if hs[0] != handshakeType {
return nil
}
handshakeLen := uint32(hs[1])<<16 | uint32(hs[2])<<8 | uint32(hs[3])
if len(hs[4:]) != int(handshakeLen) {
return nil
}
tlsVersion := uint16(hs[4])<<8 | uint16(hs[5])
if tlsVersion&tlsVersionBitmask != 0x0300 && tlsVersion != tls13 {
return nil
}
sessionIDLen := hs[38]
if len(hs) < handshakeHeaderLen+randomDataLen+sessionIDHeaderLen+int(sessionIDLen) {
return nil
}
cs := hs[handshakeHeaderLen+randomDataLen+sessionIDHeaderLen+int(sessionIDLen):]
if len(cs) < cipherSuiteHeaderLen {
return nil
}
csLen := uint16(cs[0])<<8 | uint16(cs[1])
numCiphers := int(csLen / 2)
cipherSuites := make([]uint16, 0, numCiphers)
if len(cs) < cipherSuiteHeaderLen+int(csLen)+compressMethodHeaderLen {
return nil
}
for i := 0; i < numCiphers; i++ {
cipherSuite := uint16(cs[2+i<<1])<<8 | uint16(cs[3+i<<1])
cipherSuites = append(cipherSuites, cipherSuite)
}
compressMethodLen := uint16(cs[cipherSuiteHeaderLen+int(csLen)])
if len(cs) < cipherSuiteHeaderLen+int(csLen)+compressMethodHeaderLen+int(compressMethodLen) {
return nil
}
currentIndex := cipherSuiteHeaderLen + int(csLen) + compressMethodHeaderLen + int(compressMethodLen)
serverName := indexTLSServerNameFromExtensions(cs[currentIndex:])
if serverName == nil {
return nil
}
serverName.Index += currentIndex
return serverName
}
func indexTLSServerNameFromExtensions(exs []byte) *myServerName {
if len(exs) == 0 {
return nil
}
if len(exs) < extensionsHeaderLen {
return nil
}
exsLen := uint16(exs[0])<<8 | uint16(exs[1])
exs = exs[extensionsHeaderLen:]
if len(exs) < int(exsLen) {
return nil
}
for currentIndex := extensionsHeaderLen; len(exs) > 0; {
if len(exs) < extensionHeaderLen {
return nil
}
exType := uint16(exs[0])<<8 | uint16(exs[1])
exLen := uint16(exs[2])<<8 | uint16(exs[3])
if len(exs) < extensionHeaderLen+int(exLen) {
return nil
}
sex := exs[extensionHeaderLen : extensionHeaderLen+int(exLen)]
switch exType {
case sniExtensionType:
if len(sex) < sniExtensionHeaderLen {
return nil
}
sniType := sex[2]
if sniType != sniNameDNSHostnameType {
return nil
}
sniLen := uint16(sex[3])<<8 | uint16(sex[4])
sex = sex[sniExtensionHeaderLen:]
return &myServerName{
Index: currentIndex + extensionHeaderLen + sniExtensionHeaderLen,
Length: int(sniLen),
sex: sex,
}
}
exs = exs[4+exLen:]
currentIndex += 4 + int(exLen)
}
return nil
}

View File

@@ -0,0 +1,55 @@
package tf
import (
"bufio"
"bytes"
_ "embed"
"io"
"strings"
"sync"
"github.com/sagernet/sing/common/domain"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
)
var publicPrefix = []string{
"www",
}
//go:generate wget -O public_suffix_list.dat https://publicsuffix.org/list/public_suffix_list.dat
//go:embed public_suffix_list.dat
var publicSuffix []byte
var publicSuffixMatcher = sync.OnceValue(func() *domain.Matcher {
matcher, err := initPublicSuffixMatcher()
if err != nil {
panic(F.ToString("error in initialize public suffix matcher"))
}
return matcher
})
func initPublicSuffixMatcher() (*domain.Matcher, error) {
reader := bufio.NewReader(bytes.NewReader(publicSuffix))
var domainList []string
for {
line, isPrefix, err := reader.ReadLine()
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
if isPrefix {
return nil, E.New("unexpected prefix line")
}
lineStr := string(line)
lineStr = strings.TrimSpace(lineStr)
if lineStr == "" || strings.HasPrefix(lineStr, "//") {
continue
}
domainList = append(domainList, lineStr)
}
return domain.NewMatcher(domainList, nil, false), nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,89 @@
package tf
import (
"context"
"syscall"
"time"
"github.com/sagernet/sing/common/control"
"golang.org/x/sys/unix"
)
/*
const tcpMaxNotifyAck = 10
type tcpNotifyAckID uint32
type tcpNotifyAckComplete struct {
NotifyPending uint32
NotifyCompleteCount uint32
NotifyCompleteID [tcpMaxNotifyAck]tcpNotifyAckID
}
var sizeOfTCPNotifyAckComplete = int(unsafe.Sizeof(tcpNotifyAckComplete{}))
func getsockoptTCPNotifyAckComplete(fd, level, opt int) (*tcpNotifyAckComplete, error) {
var value tcpNotifyAckComplete
vallen := uint32(sizeOfTCPNotifyAckComplete)
err := getsockopt(fd, level, opt, unsafe.Pointer(&value), &vallen)
return &value, err
}
//go:linkname getsockopt golang.org/x/sys/unix.getsockopt
func getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *uint32) error
func waitAck(ctx context.Context, conn *net.TCPConn, _ time.Duration) error {
const TCP_NOTIFY_ACKNOWLEDGEMENT = 0x212
return control.Conn(conn, func(fd uintptr) error {
err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, TCP_NOTIFY_ACKNOWLEDGEMENT, 1)
if err != nil {
if errors.Is(err, unix.EINVAL) {
return waitAckFallback(ctx, conn, 0)
}
return err
}
for {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
var ackComplete *tcpNotifyAckComplete
ackComplete, err = getsockoptTCPNotifyAckComplete(int(fd), unix.IPPROTO_TCP, TCP_NOTIFY_ACKNOWLEDGEMENT)
if err != nil {
return err
}
if ackComplete.NotifyPending == 0 {
return nil
}
time.Sleep(10 * time.Millisecond)
}
})
}
*/
func waitAck(ctx context.Context, conn syscall.Conn, fallbackDelay time.Duration) error {
return control.Conn(conn, func(fd uintptr) error {
start := time.Now()
for {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
unacked, err := unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_NWRITE)
if err != nil {
return err
}
if unacked == 0 {
if time.Since(start) <= 20*time.Millisecond {
// under transparent proxy
time.Sleep(fallbackDelay)
}
return nil
}
time.Sleep(10 * time.Millisecond)
}
})
}

View File

@@ -0,0 +1,36 @@
package tf
import (
"context"
"syscall"
"time"
"github.com/sagernet/sing/common/control"
"golang.org/x/sys/unix"
)
func waitAck(ctx context.Context, conn syscall.Conn, fallbackDelay time.Duration) error {
return control.Conn(conn, func(fd uintptr) error {
start := time.Now()
for {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
tcpInfo, err := unix.GetsockoptTCPInfo(int(fd), unix.IPPROTO_TCP, unix.TCP_INFO)
if err != nil {
return err
}
if tcpInfo.Unacked == 0 {
if time.Since(start) <= 20*time.Millisecond {
// under transparent proxy
time.Sleep(fallbackDelay)
}
return nil
}
time.Sleep(10 * time.Millisecond)
}
})
}

View File

@@ -0,0 +1,14 @@
//go:build !(linux || darwin)
package tf
import (
"context"
"syscall"
"time"
)
func waitAck(ctx context.Context, conn syscall.Conn, fallbackDelay time.Duration) error {
time.Sleep(fallbackDelay)
return nil
}

View File

@@ -22,11 +22,11 @@ const (
DNSTypeHTTPS = "https"
DNSTypeQUIC = "quic"
DNSTypeHTTP3 = "h3"
DNSTypeHosts = "hosts"
DNSTypeLocal = "local"
DNSTypePreDefined = "predefined"
DNSTypeFakeIP = "fakeip"
DNSTypeDHCP = "dhcp"
DNSTypeTailscale = "tailscale"
)
const (

View File

@@ -23,7 +23,6 @@ const (
TypeVLESS = "vless"
TypeTUIC = "tuic"
TypeHysteria2 = "hysteria2"
TypeTailscale = "tailscale"
)
const (

View File

@@ -16,6 +16,7 @@ const (
StopTimeout = 5 * time.Second
FatalStopTimeout = 10 * time.Second
FakeIPMetadataSaveInterval = 10 * time.Second
TLSFragmentFallbackDelay = 500 * time.Millisecond
)
var PortProtocols = map[uint16]string{

View File

@@ -154,6 +154,9 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int,
if isFakeIP && !allowFakeIP {
continue
}
if action.Strategy != C.DomainStrategyAsIS {
options.Strategy = action.Strategy
}
if isFakeIP || action.DisableCache {
options.DisableCache = true
}
@@ -174,6 +177,9 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int,
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action())
return transport, currentRule, currentRuleIndex
case *R.RuleActionDNSRouteOptions:
if action.Strategy != C.DomainStrategyAsIS {
options.Strategy = action.Strategy
}
if action.DisableCache {
options.DisableCache = true
}

View File

@@ -0,0 +1,63 @@
package hosts
import (
"context"
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/dns"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
mDNS "github.com/miekg/dns"
)
func RegisterTransport(registry *dns.TransportRegistry) {
dns.RegisterTransport[option.HostsDNSServerOptions](registry, C.DNSTypeHosts, NewTransport)
}
var _ adapter.DNSTransport = (*Transport)(nil)
type Transport struct {
dns.TransportAdapter
files []*File
}
func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, options option.HostsDNSServerOptions) (adapter.DNSTransport, error) {
var files []*File
if len(options.Path) == 0 {
files = append(files, NewFile(DefaultPath))
} else {
for _, path := range options.Path {
files = append(files, NewFile(path))
}
}
return &Transport{
TransportAdapter: dns.NewTransportAdapter(C.DNSTypeHosts, tag, nil),
files: files,
}, nil
}
func (t *Transport) Reset() {
}
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
question := message.Question[0]
domain := dns.FqdnToDomain(question.Name)
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
for _, file := range t.files {
addresses := file.Lookup(domain)
if len(addresses) > 0 {
return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil
}
}
}
return &mDNS.Msg{
MsgHdr: mDNS.MsgHdr{
Id: message.Id,
Rcode: mDNS.RcodeNameError,
Response: true,
},
Question: []mDNS.Question{question},
}, nil
}

View File

@@ -0,0 +1,102 @@
package hosts
import (
"bufio"
"errors"
"io"
"net/netip"
"os"
"strings"
"sync"
"time"
"github.com/miekg/dns"
)
const cacheMaxAge = 5 * time.Second
type File struct {
path string
access sync.Mutex
byName map[string][]netip.Addr
expire time.Time
modTime time.Time
size int64
}
func NewFile(path string) *File {
return &File{
path: path,
}
}
func (f *File) Lookup(name string) []netip.Addr {
f.access.Lock()
defer f.access.Unlock()
f.update()
return f.byName[name]
}
func (f *File) update() {
now := time.Now()
if now.Before(f.expire) && len(f.byName) > 0 {
return
}
stat, err := os.Stat(f.path)
if err != nil {
return
}
if f.modTime.Equal(stat.ModTime()) && f.size == stat.Size() {
f.expire = now.Add(cacheMaxAge)
return
}
byName := make(map[string][]netip.Addr)
file, err := os.Open(f.path)
if err != nil {
return
}
defer file.Close()
reader := bufio.NewReader(file)
var (
prefix []byte
line []byte
isPrefix bool
)
for {
line, isPrefix, err = reader.ReadLine()
if err != nil {
if errors.Is(err, io.EOF) {
break
}
return
}
if isPrefix {
prefix = append(prefix, line...)
continue
} else if len(prefix) > 0 {
line = append(prefix, line...)
prefix = nil
}
commentIndex := strings.IndexRune(string(line), '#')
if commentIndex != -1 {
line = line[:commentIndex]
}
fields := strings.Fields(string(line))
if len(fields) < 2 {
continue
}
var addr netip.Addr
addr, err = netip.ParseAddr(fields[0])
if err != nil {
continue
}
for index := 1; index < len(fields); index++ {
canonicalName := dns.CanonicalName(fields[index])
byName[canonicalName] = append(byName[canonicalName], addr)
}
}
f.expire = now.Add(cacheMaxAge)
f.modTime = stat.ModTime()
f.size = stat.Size()
f.byName = byName
}

View File

@@ -0,0 +1,15 @@
package hosts_test
import (
"net/netip"
"testing"
"github.com/sagernet/sing-box/dns/transport/hosts"
"github.com/stretchr/testify/require"
)
func TestHosts(t *testing.T) {
require.Equal(t, []netip.Addr{netip.AddrFrom4([4]byte{127, 0, 0, 1}), netip.IPv6Loopback()}, hosts.NewFile("testdata/hosts").Lookup("localhost."))
require.NotEmpty(t, hosts.NewFile(hosts.DefaultPath).Lookup("localhost."))
}

View File

@@ -0,0 +1,5 @@
//go:build !windows
package hosts
var DefaultPath = "/etc/hosts"

View File

@@ -0,0 +1,8 @@
package hosts
import _ "unsafe"
var DefaultPath = getSystemDirectory() + "/Drivers/etc/hosts"
//go:linkname getSystemDirectory internal/syscall/windows.GetSystemDirectory
func getSystemDirectory() string

2
dns/transport/hosts/testdata/hosts vendored Normal file
View File

@@ -0,0 +1,2 @@
127.0.0.1 localhost
::1 localhost

View File

@@ -62,9 +62,20 @@ func NewHTTPS(ctx context.Context, logger log.ContextLogger, tag string, options
if !common.Contains(tlsConfig.NextProtos(), "http/1.1") {
tlsConfig.SetNextProtos(append(tlsConfig.NextProtos(), "http/1.1"))
}
headers := options.Headers.Build()
host := headers.Get("Host")
if host != "" {
headers.Del("Host")
} else {
if tlsConfig.ServerName() != "" {
host = tlsConfig.ServerName()
} else {
host = options.Server
}
}
destinationURL := url.URL{
Scheme: "https",
Host: options.Host,
Host: host,
}
if destinationURL.Host == "" {
destinationURL.Host = options.Server
@@ -89,7 +100,7 @@ func NewHTTPS(ctx context.Context, logger log.ContextLogger, tag string, options
logger,
transportDialer,
&destinationURL,
options.Headers.Build(),
headers,
serverAddr,
tlsConfig,
), nil

View File

@@ -7,9 +7,9 @@ import (
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/dns"
"github.com/sagernet/sing-box/dns/transport/hosts"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
@@ -26,6 +26,7 @@ var _ adapter.DNSTransport = (*Transport)(nil)
type Transport struct {
dns.TransportAdapter
hosts *hosts.File
dialer N.Dialer
}
@@ -35,7 +36,8 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt
return nil, err
}
return &Transport{
TransportAdapter: dns.NewTransportAdapterWithLocalOptions(C.DNSTypeTCP, tag, options),
TransportAdapter: dns.NewTransportAdapterWithLocalOptions(C.DNSTypeLocal, tag, options),
hosts: hosts.NewFile(hosts.DefaultPath),
dialer: transportDialer,
}, nil
}
@@ -47,9 +49,9 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
question := message.Question[0]
domain := dns.FqdnToDomain(question.Name)
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
addressStrings, _ := lookupStaticHost(domain)
if len(addressStrings) > 0 {
return dns.FixedResponse(message.Id, question, common.Map(addressStrings, M.ParseAddr), C.DefaultDNSTTL), nil
addresses := t.hosts.Lookup(domain)
if len(addresses) > 0 {
return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil
}
}
systemConfig := getSystemDNSConfig()
@@ -62,7 +64,7 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
func (t *Transport) exchangeSingleRequest(ctx context.Context, systemConfig *dnsConfig, message *mDNS.Msg, domain string) (*mDNS.Msg, error) {
var lastErr error
for _, fqdn := range nameList(systemConfig, domain) {
for _, fqdn := range systemConfig.nameList(domain) {
response, err := t.tryOneName(ctx, systemConfig, fqdn, message)
if err != nil {
lastErr = err
@@ -83,6 +85,10 @@ func (t *Transport) exchangeParallel(ctx context.Context, systemConfig *dnsConfi
results := make(chan queryResult)
startRacer := func(ctx context.Context, fqdn string) {
response, err := t.tryOneName(ctx, systemConfig, fqdn, message)
addresses, _ := dns.MessageToAddresses(response)
if len(addresses) == 0 {
err = E.New(fqdn, ": empty result")
}
select {
case results <- queryResult{response, err}:
case <-returned:
@@ -90,14 +96,25 @@ func (t *Transport) exchangeParallel(ctx context.Context, systemConfig *dnsConfi
}
queryCtx, queryCancel := context.WithCancel(ctx)
defer queryCancel()
for _, fqdn := range nameList(systemConfig, domain) {
var nameCount int
for _, fqdn := range systemConfig.nameList(domain) {
nameCount++
go startRacer(queryCtx, fqdn)
}
select {
case <-ctx.Done():
return nil, ctx.Err()
case result := <-results:
return result.response, result.err
var errors []error
for {
select {
case <-ctx.Done():
return nil, ctx.Err()
case result := <-results:
if result.err == nil {
return result.response, nil
}
errors = append(errors, result.err)
if len(errors) == nameCount {
return nil, E.Errors(errors...)
}
}
}
}
@@ -118,7 +135,7 @@ func (t *Transport) tryOneName(ctx context.Context, config *dnsConfig, fqdn stri
return response, nil
}
}
return nil, lastErr
return nil, E.Cause(lastErr, fqdn)
}
func (t *Transport) exchangeOne(ctx context.Context, server M.Socksaddr, question mDNS.Question, timeout time.Duration, useTCP, ad bool) (*mDNS.Msg, error) {

View File

@@ -1,19 +0,0 @@
//go:build badlinkname
package local
import (
_ "unsafe"
)
//go:linkname getSystemDNSConfig net.getSystemDNSConfig
func getSystemDNSConfig() *dnsConfig
//go:linkname nameList net.(*dnsConfig).nameList
func nameList(c *dnsConfig, name string) []string
//go:linkname lookupStaticHost net.lookupStaticHost
func lookupStaticHost(host string) ([]string, string)
//go:linkname splitHostZone net.splitHostZone
func splitHostZone(s string) (host, zone string)

View File

@@ -1,44 +0,0 @@
package local
import (
"sync/atomic"
"time"
_ "unsafe"
)
const (
// net.maxDNSPacketSize
maxDNSPacketSize = 1232
)
type dnsConfig struct {
servers []string // server addresses (in host:port form) to use
search []string // rooted suffixes to append to local name
ndots int // number of dots in name to trigger absolute lookup
timeout time.Duration // wait before giving up on a query, including retries
attempts int // lost packets before giving up on server
rotate bool // round robin among servers
unknownOpt bool // anything unknown was encountered
lookup []string // OpenBSD top-level database "lookup" order
err error // any error that occurs during open of resolv.conf
mtime time.Time // time of resolv.conf modification
soffset uint32 // used by serverOffset
singleRequest bool // use sequential A and AAAA queries instead of parallel queries
useTCP bool // force usage of TCP for DNS resolutions
trustAD bool // add AD flag to queries
noReload bool // do not check for config file updates
}
func (c *dnsConfig) serverOffset() uint32 {
if c.rotate {
return atomic.AddUint32(&c.soffset, 1) - 1 // return 0 to start
}
return 0
}
//go:linkname runtime_rand runtime.rand
func runtime_rand() uint64
func randInt() int {
return int(uint(runtime_rand()) >> 1) // clear sign bit
}

View File

@@ -1,19 +0,0 @@
//go:build !badlinkname
package local
func getSystemDNSConfig() *dnsConfig {
panic("stub")
}
func nameList(c *dnsConfig, name string) []string {
panic("stub")
}
func lookupStaticHost(host string) ([]string, string) {
panic("stub")
}
func splitHostZone(s string) (host, zone string) {
panic("stub")
}

View File

@@ -0,0 +1,154 @@
package local
import (
"os"
"runtime"
"strings"
"sync"
"sync/atomic"
"time"
_ "unsafe"
)
const (
// net.maxDNSPacketSize
maxDNSPacketSize = 1232
)
type resolverConfig struct {
initOnce sync.Once
ch chan struct{}
lastChecked time.Time
dnsConfig atomic.Pointer[dnsConfig]
}
var resolvConf resolverConfig
func getSystemDNSConfig() *dnsConfig {
resolvConf.tryUpdate("/etc/resolv.conf")
return resolvConf.dnsConfig.Load()
}
func (conf *resolverConfig) init() {
conf.dnsConfig.Store(dnsReadConfig("/etc/resolv.conf"))
conf.lastChecked = time.Now()
conf.ch = make(chan struct{}, 1)
}
func (conf *resolverConfig) tryUpdate(name string) {
conf.initOnce.Do(conf.init)
if conf.dnsConfig.Load().noReload {
return
}
if !conf.tryAcquireSema() {
return
}
defer conf.releaseSema()
now := time.Now()
if conf.lastChecked.After(now.Add(-5 * time.Second)) {
return
}
conf.lastChecked = now
if runtime.GOOS != "windows" {
var mtime time.Time
if fi, err := os.Stat(name); err == nil {
mtime = fi.ModTime()
}
if mtime.Equal(conf.dnsConfig.Load().mtime) {
return
}
}
dnsConf := dnsReadConfig(name)
conf.dnsConfig.Store(dnsConf)
}
func (conf *resolverConfig) tryAcquireSema() bool {
select {
case conf.ch <- struct{}{}:
return true
default:
return false
}
}
func (conf *resolverConfig) releaseSema() {
<-conf.ch
}
type dnsConfig struct {
servers []string
search []string
ndots int
timeout time.Duration
attempts int
rotate bool
unknownOpt bool
lookup []string
err error
mtime time.Time
soffset uint32
singleRequest bool
useTCP bool
trustAD bool
noReload bool
}
func (c *dnsConfig) serverOffset() uint32 {
if c.rotate {
return atomic.AddUint32(&c.soffset, 1) - 1 // return 0 to start
}
return 0
}
func (conf *dnsConfig) nameList(name string) []string {
l := len(name)
rooted := l > 0 && name[l-1] == '.'
if l > 254 || l == 254 && !rooted {
return nil
}
if rooted {
if avoidDNS(name) {
return nil
}
return []string{name}
}
hasNdots := strings.Count(name, ".") >= conf.ndots
name += "."
l++
names := make([]string, 0, 1+len(conf.search))
if hasNdots && !avoidDNS(name) {
names = append(names, name)
}
for _, suffix := range conf.search {
fqdn := name + suffix
if !avoidDNS(fqdn) && len(fqdn) <= 254 {
names = append(names, fqdn)
}
}
if !hasNdots && !avoidDNS(name) {
names = append(names, name)
}
return names
}
//go:linkname runtime_rand runtime.rand
func runtime_rand() uint64
func randInt() int {
return int(uint(runtime_rand()) >> 1) // clear sign bit
}
func avoidDNS(name string) bool {
if name == "" {
return true
}
if name[len(name)-1] == '.' {
name = name[:len(name)-1]
}
return strings.HasSuffix(name, ".onion")
}

View File

@@ -0,0 +1,175 @@
//go:build !windows
package local
import (
"bufio"
"net"
"net/netip"
"os"
"strings"
"time"
_ "unsafe"
)
func dnsReadConfig(name string) *dnsConfig {
conf := &dnsConfig{
ndots: 1,
timeout: 5 * time.Second,
attempts: 2,
}
file, err := os.Open(name)
if err != nil {
conf.servers = defaultNS
conf.search = dnsDefaultSearch()
conf.err = err
return conf
}
defer file.Close()
fi, err := file.Stat()
if err == nil {
conf.mtime = fi.ModTime()
} else {
conf.servers = defaultNS
conf.search = dnsDefaultSearch()
conf.err = err
return conf
}
reader := bufio.NewReader(file)
var (
prefix []byte
line []byte
isPrefix bool
)
for {
line, isPrefix, err = reader.ReadLine()
if err != nil {
break
}
if isPrefix {
prefix = append(prefix, line...)
continue
} else if len(prefix) > 0 {
line = append(prefix, line...)
prefix = nil
}
if len(line) > 0 && (line[0] == ';' || line[0] == '#') {
continue
}
f := strings.Fields(string(line))
if len(f) < 1 {
continue
}
switch f[0] {
case "nameserver":
if len(f) > 1 && len(conf.servers) < 3 {
if _, err := netip.ParseAddr(f[1]); err == nil {
conf.servers = append(conf.servers, net.JoinHostPort(f[1], "53"))
}
}
case "domain":
if len(f) > 1 {
conf.search = []string{ensureRooted(f[1])}
}
case "search":
conf.search = make([]string, 0, len(f)-1)
for i := 1; i < len(f); i++ {
name := ensureRooted(f[i])
if name == "." {
continue
}
conf.search = append(conf.search, name)
}
case "options":
for _, s := range f[1:] {
switch {
case strings.HasPrefix(s, "ndots:"):
n, _, _ := dtoi(s[6:])
if n < 0 {
n = 0
} else if n > 15 {
n = 15
}
conf.ndots = n
case strings.HasPrefix(s, "timeout:"):
n, _, _ := dtoi(s[8:])
if n < 1 {
n = 1
}
conf.timeout = time.Duration(n) * time.Second
case strings.HasPrefix(s, "attempts:"):
n, _, _ := dtoi(s[9:])
if n < 1 {
n = 1
}
conf.attempts = n
case s == "rotate":
conf.rotate = true
case s == "single-request" || s == "single-request-reopen":
conf.singleRequest = true
case s == "use-vc" || s == "usevc" || s == "tcp":
conf.useTCP = true
case s == "trust-ad":
conf.trustAD = true
case s == "edns0":
case s == "no-reload":
conf.noReload = true
default:
conf.unknownOpt = true
}
}
case "lookup":
conf.lookup = f[1:]
default:
conf.unknownOpt = true
}
}
if len(conf.servers) == 0 {
conf.servers = defaultNS
}
if len(conf.search) == 0 {
conf.search = dnsDefaultSearch()
}
return conf
}
//go:linkname defaultNS net.defaultNS
var defaultNS []string
func dnsDefaultSearch() []string {
hn, err := os.Hostname()
if err != nil {
return nil
}
if i := strings.IndexRune(hn, '.'); i >= 0 && i < len(hn)-1 {
return []string{ensureRooted(hn[i+1:])}
}
return nil
}
func ensureRooted(s string) string {
if len(s) > 0 && s[len(s)-1] == '.' {
return s
}
return s + "."
}
const big = 0xFFFFFF
func dtoi(s string) (n int, i int, ok bool) {
n = 0
for i = 0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
n = n*10 + int(s[i]-'0')
if n >= big {
return big, i, false
}
}
if i == 0 {
return 0, 0, false
}
return n, i, true
}

View File

@@ -0,0 +1,100 @@
package local
import (
"net"
"net/netip"
"os"
"syscall"
"time"
"unsafe"
"golang.org/x/sys/windows"
)
func dnsReadConfig(_ string) *dnsConfig {
conf := &dnsConfig{
ndots: 1,
timeout: 5 * time.Second,
attempts: 2,
}
defer func() {
if len(conf.servers) == 0 {
conf.servers = defaultNS
}
}()
aas, err := adapterAddresses()
if err != nil {
return nil
}
for _, aa := range aas {
// Only take interfaces whose OperStatus is IfOperStatusUp(0x01) into DNS configs.
if aa.OperStatus != windows.IfOperStatusUp {
continue
}
// Only take interfaces which have at least one gateway
if aa.FirstGatewayAddress == nil {
continue
}
for dns := aa.FirstDnsServerAddress; dns != nil; dns = dns.Next {
sa, err := dns.Address.Sockaddr.Sockaddr()
if err != nil {
continue
}
var ip netip.Addr
switch sa := sa.(type) {
case *syscall.SockaddrInet4:
ip = netip.AddrFrom4([4]byte{sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3]})
case *syscall.SockaddrInet6:
var addr16 [16]byte
copy(addr16[:], sa.Addr[:])
if addr16[0] == 0xfe && addr16[1] == 0xc0 {
// fec0/10 IPv6 addresses are site local anycast DNS
// addresses Microsoft sets by default if no other
// IPv6 DNS address is set. Site local anycast is
// deprecated since 2004, see
// https://datatracker.ietf.org/doc/html/rfc3879
continue
}
ip = netip.AddrFrom16(addr16)
default:
// Unexpected type.
continue
}
conf.servers = append(conf.servers, net.JoinHostPort(ip.String(), "53"))
}
}
return conf
}
//go:linkname defaultNS net.defaultNS
var defaultNS []string
func adapterAddresses() ([]*windows.IpAdapterAddresses, error) {
var b []byte
l := uint32(15000) // recommended initial size
for {
b = make([]byte, l)
const flags = windows.GAA_FLAG_INCLUDE_PREFIX | windows.GAA_FLAG_INCLUDE_GATEWAYS
err := windows.GetAdaptersAddresses(syscall.AF_UNSPEC, flags, 0, (*windows.IpAdapterAddresses)(unsafe.Pointer(&b[0])), &l)
if err == nil {
if l == 0 {
return nil, nil
}
break
}
if err.(syscall.Errno) != syscall.ERROR_BUFFER_OVERFLOW {
return nil, os.NewSyscallError("getadaptersaddresses", err)
}
if l <= uint32(len(b)) {
return nil, os.NewSyscallError("getadaptersaddresses", err)
}
}
var aas []*windows.IpAdapterAddresses
for aa := (*windows.IpAdapterAddresses)(unsafe.Pointer(&b[0])); aa != nil; aa = aa.Next {
aas = append(aas, aa)
}
return aas, nil
}

View File

@@ -60,9 +60,20 @@ func NewHTTP3(ctx context.Context, logger log.ContextLogger, tag string, options
if err != nil {
return nil, err
}
headers := options.Headers.Build()
host := headers.Get("Host")
if host != "" {
headers.Del("Host")
} else {
if tlsConfig.ServerName() != "" {
host = tlsConfig.ServerName()
} else {
host = options.Server
}
}
destinationURL := url.URL{
Scheme: "HTTP3",
Host: options.Host,
Host: host,
}
if destinationURL.Host == "" {
destinationURL.Host = options.Server
@@ -87,7 +98,7 @@ func NewHTTP3(ctx context.Context, logger log.ContextLogger, tag string, options
logger: logger,
dialer: transportDialer,
destination: &destinationURL,
headers: options.Headers.Build(),
headers: headers,
transport: &http3.Transport{
Dial: func(ctx context.Context, addr string, tlsCfg *tls.STDConfig, cfg *quic.Config) (quic.EarlyConnection, error) {
destinationAddr := M.ParseSocksaddr(addr)

View File

@@ -2,7 +2,7 @@
icon: material/alert-decagram
---
#### 1.11.0-beta.23
#### 1.11.0-beta.24
* Fixes and improvements

71
go.mod
View File

@@ -1,8 +1,6 @@
module github.com/sagernet/sing-box
go 1.23.1
toolchain go1.23.2
go 1.20
require (
github.com/caddyserver/certmagic v0.20.0
@@ -29,6 +27,7 @@ require (
github.com/sagernet/quic-go v0.48.2-beta.1
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
github.com/sagernet/sing v0.6.0-beta.12
github.com/sagernet/sing-dns v0.4.0-beta.2
github.com/sagernet/sing-mux v0.3.0-alpha.1
github.com/sagernet/sing-quic v0.4.0-beta.4
github.com/sagernet/sing-shadowsocks v0.2.7
@@ -37,7 +36,6 @@ require (
github.com/sagernet/sing-tun v0.6.0-beta.8
github.com/sagernet/sing-vmess v0.2.0-beta.2
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
github.com/sagernet/tailscale v0.0.0-20241203114627-8b68177dbcc1
github.com/sagernet/utls v1.6.7
github.com/sagernet/wireguard-go v0.0.1-beta.5
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
@@ -56,86 +54,51 @@ require (
howett.net/plist v1.0.1
)
//replace github.com/sagernet/sing => ../sing
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/ajg/form v1.5.1 // indirect
github.com/akutz/memconn v0.1.0 // indirect
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/bits-and-blooms/bitset v1.13.0 // indirect
github.com/andybalholm/brotli v1.0.6 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/coder/websocket v1.8.12 // indirect
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fxamacker/cbor/v2 v2.6.0 // indirect
github.com/gaissmai/bart v0.11.1 // indirect
github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/csrf v1.7.2 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect
github.com/hashicorp/yamux v0.1.2 // indirect
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
github.com/illarion/gonotify/v2 v2.0.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 // indirect
github.com/jsimonetti/rtnetlink v1.4.0 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/compress v1.17.4 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a // indirect
github.com/libdns/libdns v0.2.2 // indirect
github.com/mdlayher/genetlink v1.3.2 // indirect
github.com/mdlayher/netlink v1.7.2 // indirect
github.com/mdlayher/sdnotify v1.0.0 // indirect
github.com/mdlayher/socket v0.5.0 // indirect
github.com/mitchellh/go-ps v1.0.0 // indirect
github.com/onsi/ginkgo/v2 v2.17.2 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus-community/pro-bing v0.4.0 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/onsi/ginkgo/v2 v2.9.7 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
github.com/safchain/ethtool v0.3.0 // indirect
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
github.com/tailscale/golang-x-crypto v0.0.0-20240604161659-3fde5e568aa4 // indirect
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect
github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 // indirect
github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 // indirect
github.com/tcnksm/go-httpstat v0.2.0 // indirect
github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e // indirect
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
github.com/vishvananda/netns v0.0.4 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect
go.uber.org/multierr v1.11.0 // indirect
go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/term v0.27.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.7.0 // indirect
golang.org/x/tools v0.24.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/blake3 v1.3.0 // indirect
)

163
go.sum
View File

@@ -1,69 +1,39 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A=
github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc=
github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk=
github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 h1:8h5+bWd7R6AYUslN6c6iuZWTKsKxUFDlpnmilO6R2n0=
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa h1:h8TfIT1xc8FWbwwpmHn1J5i43Y0uZP97GqasGCzSRJk=
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa/go.mod h1:Nx87SkVqTKd8UtT+xu7sM/l+LgXs6c0aHrlKusR+2EQ=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbYd8tQGRWacE9kU=
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e h1:vUmf0yezR0y7jJ5pceLHthLaYf4bA5T14B6q39S4q2Q=
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA=
github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/gaissmai/bart v0.11.1 h1:5Uv5XwsaFBRo4E5VBcb9TzY8B7zxFf+U7isDxqOrRfc=
github.com/gaissmai/bart v0.11.1/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg=
github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I=
github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 h1:ymLjT4f35nQbASLnvxEde4XOBL+Sn7rFuV+FOJqkljg=
github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 h1:sQspH8M4niEijh3PFscJRLDnkL547IeP7kpPe3uUhEg=
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466/go.mod h1:ZiQxhyQ+bbbfxUKVvjfO498oPYvtYhZzycal3G/NHmU=
github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk=
github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -71,41 +41,26 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 h1:wG8RYIyctLhdFk6Vl1yPGtSRtwGpVkWyZww1OCil2MI=
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4=
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI=
github.com/gorilla/csrf v1.7.2/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU=
github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo=
github.com/illarion/gonotify/v2 v2.0.3 h1:B6+SKPo/0Sw8cRJh1aLzNEeNVFfzE3c6N+o+vyxM+9A=
github.com/illarion/gonotify/v2 v2.0.3/go.mod h1:38oIJTgFqupkEydkkClkbL6i5lXV/bxdH9do5TALPEE=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA=
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk=
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8=
github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I=
github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
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/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a h1:+RR6SqnTkDLWyICxS1xpjCi/3dhyV+TgZwA6Ww3KncQ=
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a/go.mod h1:YTtCCM3ryyfiu4F7t8HQ1mxvp1UBdWM2r6Xa+nGWvDk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ=
github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE=
github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn054=
@@ -115,45 +70,32 @@ github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw=
github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o=
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ3c=
github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE=
github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI=
github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa h1:9mcjV+RGZVC3reJBNDjjNPyS8PmFG97zq56X7WNaFO4=
github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa/go.mod h1:4tLB5c8U0CxpkFM+AJJB77jEaVDbLH5XQvy42vAGsWw=
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g=
github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss=
github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0=
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus-community/pro-bing v0.4.0 h1:YMbv+i08gQz97OZZBwLyvmmQEEzyfyrrjEaAchdy3R4=
github.com/prometheus-community/pro-bing v0.4.0/go.mod h1:b7wRYZtCcPmt4Sz319BykUU241rWLe1VFXyiyWK/dH4=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0=
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1 h1:qi+ijeREa0yfAaO+NOcZ81gv4uzOfALUIdhkiIFvmG4=
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1/go.mod h1:JULDuzTMn2gyZFcjpTVZP4/UuwAdbHJ0bum2RdjXojU=
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0=
@@ -179,6 +121,8 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4Wk
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
github.com/sagernet/sing v0.6.0-beta.12 h1:2DnTJcvypK3/PM/8JjmgG8wVK48gdcpRwU98c4J/a7s=
github.com/sagernet/sing v0.6.0-beta.12/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-dns v0.4.0-beta.2 h1:HW94bUEp7K/vf5DlYz646LTZevQtJ0250jZa/UZRlbY=
github.com/sagernet/sing-dns v0.4.0-beta.2/go.mod h1:8wuFcoFkWM4vJuQyg8e97LyvDwe0/Vl7G839WLcKDs8=
github.com/sagernet/sing-mux v0.3.0-alpha.1 h1:IgNX5bJBpL41gGbp05pdDOvh/b5eUQ6cv9240+Ngipg=
github.com/sagernet/sing-mux v0.3.0-alpha.1/go.mod h1:FTcImmdfW38Lz7b+HQ+mxxOth1lz4ao8uEnz+MwIJQE=
github.com/sagernet/sing-quic v0.4.0-beta.4 h1:kKiMLGaxvVLDCSvCMYo4PtWd1xU6FTL7xvUAQfXO09g=
@@ -195,8 +139,6 @@ github.com/sagernet/sing-vmess v0.2.0-beta.2 h1:obAkAL35X7ql4RnGzDg4dBYIRpGXRKqc
github.com/sagernet/sing-vmess v0.2.0-beta.2/go.mod h1:HGhf9XUdeE2iOWrX0hQNFgXPbKyGlzpeYFyX0c/pykk=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
github.com/sagernet/tailscale v0.0.0-20241203114627-8b68177dbcc1 h1:7KzocP8ewushqpf/zsgt3LnSevK5IPNPorb/lfT6RYY=
github.com/sagernet/tailscale v0.0.0-20241203114627-8b68177dbcc1/go.mod h1:xIn0nkXVWp45voGMMzAXvgzwsQ+2CGCiTt9LkHONbSE=
github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8=
github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM=
github.com/sagernet/wireguard-go v0.0.1-beta.5 h1:aBEsxJUMEONwOZqKPIkuAcv4zJV5p6XlzEN04CF0FXc=
@@ -208,36 +150,14 @@ github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3k
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e h1:PtWT87weP5LWHEY//SWsYkSO3RWRZo4OSWagh3YD2vQ=
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4=
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4=
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg=
github.com/tailscale/golang-x-crypto v0.0.0-20240604161659-3fde5e568aa4 h1:rXZGgEa+k2vJM8xT0PoSKfVXwFGPQ3z3CJfmnHJkZZw=
github.com/tailscale/golang-x-crypto v0.0.0-20240604161659-3fde5e568aa4/go.mod h1:ikbF+YT089eInTp9f2vmvy4+ZVnW5hzX1q2WknxSprQ=
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 h1:4chzWmimtJPxRs2O36yuGRW3f9SYV+bMTTvMBI0EKio=
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8=
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw=
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8=
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 h1:uFsXVBE9Qr4ZoF094vE6iYTLDl0qCiKzYXlL6UeWObU=
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0=
github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 h1:Gz0rz40FvFVLTBk/K8UNAenb36EbDSnh+q7Z9ldcC8w=
github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4/go.mod h1:phI29ccmHQBc+wvroosENp1IF9195449VDnFDhJ4rJU=
github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 h1:tdUdyPqJ0C97SJfjB9tW6EylTtreyee9C44de+UBG0g=
github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ=
github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA=
github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk=
github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0=
github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8=
github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e h1:BA9O3BmlTmpjbvajAwzWx4Wo2TRVdpPXZEeemGQcajw=
github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
@@ -245,13 +165,10 @@ github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvv
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8=
go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
@@ -259,25 +176,18 @@ golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
@@ -285,7 +195,6 @@ golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
@@ -295,23 +204,21 @@ golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
@@ -320,5 +227,3 @@ howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=

View File

@@ -11,6 +11,7 @@ import (
"github.com/sagernet/sing-box/dns"
"github.com/sagernet/sing-box/dns/transport"
"github.com/sagernet/sing-box/dns/transport/fakeip"
"github.com/sagernet/sing-box/dns/transport/hosts"
"github.com/sagernet/sing-box/dns/transport/local"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
@@ -91,7 +92,6 @@ func EndpointRegistry() *endpoint.Registry {
registry := endpoint.NewRegistry()
registerWireGuardEndpoint(registry)
registerTailscaleEndpoint(registry)
return registry
}
@@ -104,12 +104,12 @@ func DNSTransportRegistry() *dns.TransportRegistry {
transport.RegisterTLS(registry)
transport.RegisterHTTPS(registry)
transport.RegisterPredefined(registry)
hosts.RegisterTransport(registry)
local.RegisterTransport(registry)
fakeip.RegisterTransport(registry)
registerQUICTransports(registry)
registerDHCPTransport(registry)
registerTailscaleTransport(registry)
return registry
}

View File

@@ -1,17 +0,0 @@
//go:build with_tailscale
package include
import (
"github.com/sagernet/sing-box/adapter/endpoint"
"github.com/sagernet/sing-box/dns"
"github.com/sagernet/sing-box/protocol/tailscale"
)
func registerTailscaleEndpoint(registry *endpoint.Registry) {
tailscale.RegisterEndpoint(registry)
}
func registerTailscaleTransport(registry *dns.TransportRegistry) {
tailscale.RegistryTransport(registry)
}

View File

@@ -1,27 +0,0 @@
//go:build !with_tailscale
package include
import (
"context"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/adapter/endpoint"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/dns"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
)
func registerTailscaleEndpoint(registry *endpoint.Registry) {
endpoint.Register[option.TailscaleEndpointOptions](registry, C.TypeTailscale, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TailscaleEndpointOptions) (adapter.Endpoint, error) {
return nil, E.New(`Tailscale is not included in this build, rebuild with -tags with_tailscale`)
})
}
func registerTailscaleTransport(registry *dns.TransportRegistry) {
dns.RegisterTransport[option.TailscaleDNSServerOptions](registry, C.DNSTypeTailscale, func(ctx context.Context, logger log.ContextLogger, tag string, options option.TailscaleDNSServerOptions) (adapter.DNSTransport, error) {
return nil, E.New(`Tailscale is not included in this build, rebuild with -tags with_tailscale`)
})
}

View File

@@ -259,6 +259,11 @@ type LegacyDNSServerOptions struct {
ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
}
type HostsDNSServerOptions struct {
Path badoption.Listable[string] `json:"path,omitempty"`
Predefined badjson.TypedMap[string, badoption.Listable[netip.Addr]] `json:"predefined,omitempty"`
}
type LocalDNSServerOptions struct {
DialerOptions
LegacyStrategy DomainStrategy `json:"-"`
@@ -281,7 +286,6 @@ type RemoteTLSDNSServerOptions struct {
type RemoteHTTPSDNSServerOptions struct {
RemoteTLSDNSServerOptions
Host string `json:"host,omitempty"`
Path string `json:"path,omitempty"`
Method string `json:"method,omitempty"`
Headers badoption.HTTPHeader `json:"headers,omitempty"`

View File

@@ -150,6 +150,9 @@ type RawRouteOptionsActionOptions struct {
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
UDPConnect bool `json:"udp_connect,omitempty"`
UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"`
TLSFragment bool `json:"tls_fragment,omitempty"`
TLSFragmentFallbackDelay badoption.Duration `json:"tls_fragment_fallback_delay,omitempty"`
}
type RouteOptionsActionOptions RawRouteOptionsActionOptions
@@ -167,12 +170,14 @@ func (r *RouteOptionsActionOptions) UnmarshalJSON(data []byte) error {
type DNSRouteActionOptions struct {
Server string `json:"server,omitempty"`
Strategy DomainStrategy `json:"strategy,omitempty"`
DisableCache bool `json:"disable_cache,omitempty"`
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
}
type _DNSRouteOptionsActionOptions struct {
Strategy DomainStrategy `json:"strategy,omitempty"`
DisableCache bool `json:"disable_cache,omitempty"`
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`

View File

@@ -83,6 +83,7 @@ type RawDefaultDNSRule struct {
GeoIP badoption.Listable[string] `json:"geoip,omitempty"`
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
IPIsPrivate bool `json:"ip_is_private,omitempty"`
IPAcceptAny bool `json:"ip_accept_any,omitempty"`
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`

View File

@@ -1,25 +0,0 @@
package option
import (
"net/netip"
)
type TailscaleEndpointOptions struct {
StateDirectory string `json:"state_directory,omitempty"`
AuthKey string `json:"auth_key,omitempty"`
ControlURL string `json:"control_url,omitempty"`
Ephemeral bool `json:"ephemeral,omitempty"`
Hostname string `json:"hostname,omitempty"`
ExitNode string `json:"exit_node,omitempty"`
ExitNodeAllowLANAccess bool `json:"exit_node_allow_lan_access,omitempty"`
AdvertiseRoutes []netip.Prefix `json:"advertise_routes,omitempty"`
AdvertiseExitNode bool `json:"advertise_exit_node,omitempty"`
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
}
type TailscaleDNSServerOptions struct {
Endpoint string `json:"endpoint,omitempty"`
AcceptDefaultResolvers bool `json:"accept_default_resolvers,omitempty"`
}

View File

@@ -1,316 +0,0 @@
package tailscale
import (
"context"
"net"
"net/http"
"net/netip"
"net/url"
"os"
"reflect"
"strings"
"sync"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer"
"github.com/sagernet/sing-box/common/tls"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/dns"
"github.com/sagernet/sing-box/dns/transport"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/service"
nDNS "github.com/sagernet/tailscale/net/dns"
"github.com/sagernet/tailscale/types/dnstype"
"github.com/sagernet/tailscale/wgengine/router"
"github.com/sagernet/tailscale/wgengine/wgcfg"
mDNS "github.com/miekg/dns"
"go4.org/netipx"
"golang.org/x/net/http2"
)
func RegistryTransport(registry *dns.TransportRegistry) {
dns.RegisterTransport[option.TailscaleDNSServerOptions](registry, C.DNSTypeTailscale, NewDNSTransport)
}
type DNSTransport struct {
dns.TransportAdapter
ctx context.Context
logger logger.ContextLogger
endpointTag string
acceptDefaultResolvers bool
dnsRouter adapter.DNSRouter
endpointManager adapter.EndpointManager
cfg *wgcfg.Config
dnsCfg *nDNS.Config
endpoint *Endpoint
routePrefixes []netip.Prefix
routes map[string][]adapter.DNSTransport
hosts map[string][]netip.Addr
defaultResolvers []adapter.DNSTransport
}
func NewDNSTransport(ctx context.Context, logger log.ContextLogger, tag string, options option.TailscaleDNSServerOptions) (adapter.DNSTransport, error) {
if options.Endpoint == "" {
return nil, E.New("missing tailscale endpoint tag")
}
return &DNSTransport{
TransportAdapter: dns.NewTransportAdapter(C.DNSTypeTailscale, tag, nil),
ctx: ctx,
logger: logger,
endpointTag: options.Endpoint,
acceptDefaultResolvers: options.AcceptDefaultResolvers,
dnsRouter: service.FromContext[adapter.DNSRouter](ctx),
endpointManager: service.FromContext[adapter.EndpointManager](ctx),
}, nil
}
func (t *DNSTransport) Start(stage adapter.StartStage) error {
if stage != adapter.StartStateInitialize {
return nil
}
rawOutbound, loaded := t.endpointManager.Get(t.endpointTag)
if !loaded {
return E.New("endpoint not found: ", t.endpointTag)
}
ep, isTailscale := rawOutbound.(*Endpoint)
if !isTailscale {
return E.New("endpoint is not tailscale: ", t.endpointTag)
}
if ep.onReconfig != nil {
return E.New("only one tailscale DNS server is allowed for single endpoint")
}
ep.onReconfig = t.onReconfig
t.endpoint = ep
return nil
}
func (t *DNSTransport) Reset() {
}
func (t *DNSTransport) onReconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCfg *nDNS.Config) {
if cfg == nil || dnsCfg == nil {
return
}
if (t.cfg != nil && reflect.DeepEqual(t.cfg, cfg)) && (t.dnsCfg != nil && reflect.DeepEqual(t.dnsCfg, dnsCfg)) {
return
}
t.cfg = cfg
t.dnsCfg = dnsCfg
err := t.updateDNSServers(routerCfg, dnsCfg)
if err != nil {
t.logger.Error(E.Cause(err, "update DNS servers"))
}
}
func (t *DNSTransport) updateDNSServers(routeConfig *router.Config, dnsConfig *nDNS.Config) error {
t.routePrefixes = buildRoutePrefixes(routeConfig)
directDialerOnce := sync.OnceValue(func() N.Dialer {
directDialer := common.Must1(dialer.NewDefault(t.ctx, option.DialerOptions{}))
return &DNSDialer{transport: t, fallbackDialer: directDialer}
})
routes := make(map[string][]adapter.DNSTransport)
for domain, resolvers := range dnsConfig.Routes {
var myResolvers []adapter.DNSTransport
for _, resolver := range resolvers {
myResolver, err := t.createResolver(directDialerOnce, resolver)
if err != nil {
return err
}
myResolvers = append(myResolvers, myResolver)
}
routes[domain.WithTrailingDot()] = myResolvers
}
hosts := make(map[string][]netip.Addr)
for domain, addresses := range dnsConfig.Hosts {
hosts[domain.WithTrailingDot()] = addresses
}
var defaultResolvers []adapter.DNSTransport
for _, resolver := range dnsConfig.DefaultResolvers {
myResolver, err := t.createResolver(directDialerOnce, resolver)
if err != nil {
return err
}
defaultResolvers = append(defaultResolvers, myResolver)
}
t.routes = routes
t.hosts = hosts
t.defaultResolvers = defaultResolvers
if len(defaultResolvers) > 0 {
t.logger.Info("updated ", len(routes), " routes, ", len(hosts), " hosts, default resolvers: ",
strings.Join(common.Map(dnsConfig.DefaultResolvers, func(it *dnstype.Resolver) string { return it.Addr }), " "))
} else {
t.logger.Info("updated ", len(routes), " routes, ", len(hosts), " hosts")
}
return nil
}
func (t *DNSTransport) createResolver(directDialer func() N.Dialer, resolver *dnstype.Resolver) (adapter.DNSTransport, error) {
serverURL, parseURLErr := url.Parse(resolver.Addr)
var myDialer N.Dialer
if parseURLErr == nil && serverURL.Scheme == "http" {
myDialer = t.endpoint
} else {
myDialer = directDialer()
}
if len(resolver.BootstrapResolution) > 0 {
bootstrapTransport := transport.NewUDPRaw(t.logger, t.TransportAdapter, myDialer, M.SocksaddrFrom(resolver.BootstrapResolution[0], 53))
myDialer = dns.NewTransportDialer(myDialer, t.dnsRouter, bootstrapTransport, C.DomainStrategyPreferIPv4, 0)
}
if serverAddr := M.ParseSocksaddr(resolver.Addr); serverAddr.IsValid() {
if serverAddr.Port == 0 {
serverAddr.Port = 53
}
return transport.NewUDPRaw(t.logger, t.TransportAdapter, myDialer, serverAddr), nil
} else if parseURLErr != nil {
return nil, E.Cause(parseURLErr, "parse resolver address")
} else {
switch serverURL.Scheme {
case "https":
serverAddr = M.ParseSocksaddrHostPortStr(serverURL.Hostname(), serverURL.Port())
if serverAddr.Port == 0 {
serverAddr.Port = 443
}
tlsConfig := common.Must1(tls.NewClient(t.ctx, serverAddr.AddrString(), option.OutboundTLSOptions{
ALPN: []string{http2.NextProtoTLS, "http/1.1"},
}))
return transport.NewHTTPSRaw(t.TransportAdapter, t.logger, myDialer, serverURL, http.Header{}, serverAddr, tlsConfig), nil
case "http":
serverAddr = M.ParseSocksaddrHostPortStr(serverURL.Hostname(), serverURL.Port())
if serverAddr.Port == 0 {
serverAddr.Port = 80
}
return transport.NewHTTPSRaw(t.TransportAdapter, t.logger, myDialer, serverURL, http.Header{}, serverAddr, nil), nil
// case "tls":
default:
return nil, E.New("unknown resolver scheme: ", serverURL.Scheme)
}
}
}
func buildRoutePrefixes(routeConfig *router.Config) []netip.Prefix {
var builder netipx.IPSetBuilder
for _, localAddr := range routeConfig.LocalAddrs {
builder.AddPrefix(localAddr)
}
for _, route := range routeConfig.Routes {
builder.AddPrefix(route)
}
for _, route := range routeConfig.LocalRoutes {
builder.AddPrefix(route)
}
for _, route := range routeConfig.SubnetRoutes {
builder.AddPrefix(route)
}
ipSet, err := builder.IPSet()
if err != nil {
return nil
}
return ipSet.Prefixes()
}
func (t *DNSTransport) Close() error {
return nil
}
func (t *DNSTransport) Raw() bool {
return true
}
func (t *DNSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
if len(message.Question) != 1 {
return nil, os.ErrInvalid
}
question := message.Question[0]
addresses, hostsLoaded := t.hosts[question.Name]
if hostsLoaded {
switch question.Qtype {
case mDNS.TypeA:
addresses4 := common.Filter(addresses, func(addr netip.Addr) bool {
return addr.Is4()
})
if len(addresses4) > 0 {
return dns.FixedResponse(message.Id, question, addresses4, C.DefaultDNSTTL), nil
}
case mDNS.TypeAAAA:
addresses6 := common.Filter(addresses, func(addr netip.Addr) bool {
return addr.Is6()
})
if len(addresses6) > 0 {
return dns.FixedResponse(message.Id, question, addresses6, C.DefaultDNSTTL), nil
}
}
}
for domainSuffix, transports := range t.routes {
if strings.HasSuffix(question.Name, domainSuffix) {
if len(transports) == 0 {
return &mDNS.Msg{
MsgHdr: mDNS.MsgHdr{
Id: message.Id,
Rcode: mDNS.RcodeNameError,
Response: true,
},
Question: []mDNS.Question{question},
}, nil
}
var lastErr error
for _, dnsTransport := range transports {
response, err := dnsTransport.Exchange(ctx, message)
if err != nil {
lastErr = err
continue
}
return response, nil
}
return nil, lastErr
}
}
if t.acceptDefaultResolvers && len(t.defaultResolvers) > 0 {
var lastErr error
for _, resolver := range t.defaultResolvers {
response, err := resolver.Exchange(ctx, message)
if err != nil {
lastErr = err
continue
}
return response, nil
}
return nil, lastErr
}
return nil, dns.RCodeNameError
}
type DNSDialer struct {
transport *DNSTransport
fallbackDialer N.Dialer
}
func (d *DNSDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
if destination.IsFqdn() {
panic("invalid request here")
}
for _, prefix := range d.transport.routePrefixes {
if prefix.Contains(destination.Addr) {
return d.transport.endpoint.DialContext(ctx, network, destination)
}
}
return d.fallbackDialer.DialContext(ctx, network, destination)
}
func (d *DNSDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
if destination.IsFqdn() {
panic("invalid request here")
}
for _, prefix := range d.transport.routePrefixes {
if prefix.Contains(destination.Addr) {
return d.transport.endpoint.ListenPacket(ctx, destination)
}
}
return d.fallbackDialer.ListenPacket(ctx, destination)
}

View File

@@ -1,425 +0,0 @@
package tailscale
import (
"context"
"fmt"
"net"
"net/netip"
"os"
"path/filepath"
"runtime"
"strings"
"sync/atomic"
"syscall"
"time"
"github.com/sagernet/gvisor/pkg/tcpip"
"github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet"
"github.com/sagernet/gvisor/pkg/tcpip/header"
"github.com/sagernet/gvisor/pkg/tcpip/stack"
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/adapter/endpoint"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/experimental/libbox/platform"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/service"
"github.com/sagernet/sing/service/filemanager"
"github.com/sagernet/tailscale/ipn"
"github.com/sagernet/tailscale/net/netmon"
"github.com/sagernet/tailscale/net/tsaddr"
"github.com/sagernet/tailscale/tsnet"
"github.com/sagernet/tailscale/types/ipproto"
"github.com/sagernet/tailscale/version"
"github.com/sagernet/tailscale/wgengine"
"github.com/sagernet/tailscale/wgengine/filter"
)
func init() {
version.SetVersion("sing-box " + C.Version)
}
func RegisterEndpoint(registry *endpoint.Registry) {
endpoint.Register[option.TailscaleEndpointOptions](registry, C.TypeTailscale, NewEndpoint)
}
type Endpoint struct {
endpoint.Adapter
ctx context.Context
router adapter.Router
logger logger.ContextLogger
dnsRouter adapter.DNSRouter
network adapter.NetworkManager
platformInterface platform.Interface
server *tsnet.Server
stack *stack.Stack
filter *atomic.Pointer[filter.Filter]
onReconfig wgengine.ReconfigListener
exitNode string
exitNodeAllowLANAccess bool
advertiseRoutes []netip.Prefix
advertiseExitNode bool
udpTimeout time.Duration
}
func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TailscaleEndpointOptions) (adapter.Endpoint, error) {
stateDirectory := options.StateDirectory
if stateDirectory == "" {
stateDirectory = "tailscale"
}
hostname := options.Hostname
if hostname == "" {
osHostname, _ := os.Hostname()
osHostname = strings.TrimSpace(osHostname)
hostname = osHostname
}
if hostname == "" {
hostname = "sing-box"
}
stateDirectory = filemanager.BasePath(ctx, os.ExpandEnv(stateDirectory))
stateDirectory, _ = filepath.Abs(stateDirectory)
server := &tsnet.Server{
Dir: stateDirectory,
Hostname: hostname,
Logf: func(format string, args ...any) {
logger.Trace(fmt.Sprintf(format, args...))
},
UserLogf: func(format string, args ...any) {
logger.Debug(fmt.Sprintf(format, args...))
},
Ephemeral: options.Ephemeral,
AuthKey: options.AuthKey,
ControlURL: options.ControlURL,
}
for _, advertiseRoute := range options.AdvertiseRoutes {
if advertiseRoute.Addr().IsUnspecified() && advertiseRoute.Bits() == 0 {
return nil, E.New("`advertise_routes` cannot be default, use `advertise_exit_node` instead.")
}
}
if options.AdvertiseExitNode && options.ExitNode != "" {
return nil, E.New("cannot advertise an exit node and use an exit node at the same time.")
}
var udpTimeout time.Duration
if options.UDPTimeout != 0 {
udpTimeout = time.Duration(options.UDPTimeout)
} else {
udpTimeout = C.UDPTimeout
}
return &Endpoint{
Adapter: endpoint.NewAdapter(C.TypeTailscale, tag, []string{N.NetworkTCP, N.NetworkUDP}, nil),
ctx: ctx,
router: router,
logger: logger,
dnsRouter: service.FromContext[adapter.DNSRouter](ctx),
network: service.FromContext[adapter.NetworkManager](ctx),
platformInterface: service.FromContext[platform.Interface](ctx),
server: server,
exitNode: options.ExitNode,
exitNodeAllowLANAccess: options.ExitNodeAllowLANAccess,
advertiseRoutes: options.AdvertiseRoutes,
advertiseExitNode: options.AdvertiseExitNode,
udpTimeout: udpTimeout,
}, nil
}
func (t *Endpoint) Start(stage adapter.StartStage) error {
if stage != adapter.StartStateStart {
return nil
}
if t.platformInterface != nil {
err := t.network.UpdateInterfaces()
if err != nil {
return err
}
netmon.RegisterInterfaceGetter(func() ([]netmon.Interface, error) {
return common.Map(t.network.InterfaceFinder().Interfaces(), func(it control.Interface) netmon.Interface {
return netmon.Interface{
Interface: &net.Interface{
Index: it.Index,
MTU: it.MTU,
Name: it.Name,
HardwareAddr: it.HardwareAddr,
Flags: it.Flags,
},
AltAddrs: common.Map(it.Addresses, func(it netip.Prefix) net.Addr {
return &net.IPNet{
IP: it.Addr().AsSlice(),
Mask: net.CIDRMask(it.Bits(), it.Addr().BitLen()),
}
}),
}
}), nil
})
if runtime.GOOS == "android" {
setAndroidProtectFunc(t.platformInterface)
}
}
err := t.server.Start()
if err != nil {
return err
}
if t.onReconfig != nil {
t.server.ExportLocalBackend().ExportEngine().(wgengine.ExportedUserspaceEngine).SetOnReconfigListener(t.onReconfig)
}
ipStack := t.server.ExportNetstack().ExportIPStack()
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tun.NewTCPForwarder(t.ctx, ipStack, t).HandlePacket)
udpForwarder := tun.NewUDPForwarder(t.ctx, ipStack, t, t.udpTimeout)
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
t.stack = ipStack
localBackend := t.server.ExportLocalBackend()
perfs := &ipn.MaskedPrefs{
ExitNodeIPSet: true,
AdvertiseRoutesSet: true,
}
if len(t.advertiseRoutes) > 0 {
perfs.AdvertiseRoutes = t.advertiseRoutes
}
if t.advertiseExitNode {
perfs.AdvertiseRoutes = append(perfs.AdvertiseRoutes, tsaddr.ExitRoutes()...)
}
_, err = localBackend.EditPrefs(perfs)
if err != nil {
return E.Cause(err, "update prefs")
}
t.filter = localBackend.ExportFilter()
go t.watchState()
return nil
}
func (t *Endpoint) watchState() {
localBackend := t.server.ExportLocalBackend()
localBackend.WatchNotifications(t.ctx, ipn.NotifyInitialState, nil, func(roNotify *ipn.Notify) (keepGoing bool) {
if roNotify.State != nil && *roNotify.State != ipn.NeedsLogin && *roNotify.State != ipn.NoState {
return false
}
authURL := localBackend.StatusWithoutPeers().AuthURL
if authURL != "" {
t.logger.Info("Waiting for authentication: ", authURL)
if t.platformInterface != nil {
err := t.platformInterface.SendNotification(&platform.Notification{
Identifier: "tailscale-authentication",
TypeName: "Tailscale Authentication Notifications",
TypeID: 10,
Title: "Tailscale Authentication",
Body: F.ToString("Tailscale outbound[", t.Tag(), "] is waiting for authentication."),
OpenURL: authURL,
})
if err != nil {
t.logger.Error("send authentication notification: ", err)
}
}
return false
}
return true
})
if t.exitNode != "" {
localBackend.WatchNotifications(t.ctx, ipn.NotifyInitialState, nil, func(roNotify *ipn.Notify) (keepGoing bool) {
if roNotify.State == nil || *roNotify.State != ipn.Running {
return true
}
status, err := common.Must1(t.server.LocalClient()).Status(t.ctx)
if err != nil {
t.logger.Error("set exit node: ", err)
return
}
perfs := &ipn.MaskedPrefs{
Prefs: ipn.Prefs{
ExitNodeAllowLANAccess: t.exitNodeAllowLANAccess,
},
ExitNodeIPSet: true,
ExitNodeAllowLANAccessSet: true,
}
err = perfs.SetExitNodeIP(t.exitNode, status)
if err != nil {
t.logger.Error("set exit node: ", err)
return true
}
_, err = localBackend.EditPrefs(perfs)
if err != nil {
t.logger.Error("set exit node: ", err)
return true
}
return false
})
}
}
func (t *Endpoint) Close() error {
netmon.RegisterInterfaceGetter(nil)
if runtime.GOOS == "android" {
setAndroidProtectFunc(nil)
}
return common.Close(common.PtrOrNil(t.server))
}
func (t *Endpoint) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
switch network {
case N.NetworkTCP:
t.logger.InfoContext(ctx, "outbound connection to ", destination)
case N.NetworkUDP:
t.logger.InfoContext(ctx, "outbound packet connection to ", destination)
}
if destination.IsFqdn() {
destinationAddresses, err := t.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{})
if err != nil {
return nil, err
}
return N.DialSerial(ctx, t, network, destination, destinationAddresses)
}
addr := tcpip.FullAddress{
NIC: 1,
Port: destination.Port,
Addr: addressFromAddr(destination.Addr),
}
var networkProtocol tcpip.NetworkProtocolNumber
if destination.IsIPv4() {
networkProtocol = header.IPv4ProtocolNumber
} else {
networkProtocol = header.IPv6ProtocolNumber
}
switch N.NetworkName(network) {
case N.NetworkTCP:
tcpConn, err := gonet.DialContextTCP(ctx, t.stack, addr, networkProtocol)
if err != nil {
return nil, err
}
return tcpConn, nil
case N.NetworkUDP:
udpConn, err := gonet.DialUDP(t.stack, nil, &addr, networkProtocol)
if err != nil {
return nil, err
}
return udpConn, nil
default:
return nil, E.Extend(N.ErrUnknownNetwork, network)
}
}
func (t *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
t.logger.InfoContext(ctx, "outbound packet connection to ", destination)
if destination.IsFqdn() {
destinationAddresses, err := t.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{})
if err != nil {
return nil, err
}
packetConn, _, err := N.ListenSerial(ctx, t, destination, destinationAddresses)
if err != nil {
return nil, err
}
return packetConn, err
}
addr4, addr6 := t.server.TailscaleIPs()
bind := tcpip.FullAddress{
NIC: 1,
}
var networkProtocol tcpip.NetworkProtocolNumber
if destination.IsIPv4() {
if !addr4.IsValid() {
return nil, E.New("missing Tailscale IPv4 address")
}
networkProtocol = header.IPv4ProtocolNumber
bind.Addr = addressFromAddr(addr4)
} else {
if !addr6.IsValid() {
return nil, E.New("missing Tailscale IPv6 address")
}
networkProtocol = header.IPv6ProtocolNumber
bind.Addr = addressFromAddr(addr6)
}
udpConn, err := gonet.DialUDP(t.stack, &bind, nil, networkProtocol)
if err != nil {
return nil, err
}
return udpConn, nil
}
func (t *Endpoint) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr) error {
tsFilter := t.filter.Load()
if tsFilter != nil {
var ipProto ipproto.Proto
switch N.NetworkName(network) {
case N.NetworkTCP:
ipProto = ipproto.TCP
case N.NetworkUDP:
ipProto = ipproto.UDP
}
response := tsFilter.Check(source.Addr, destination.Addr, destination.Port, ipProto)
switch response {
case filter.Drop:
return syscall.ECONNRESET
case filter.DropSilently:
return tun.ErrDrop
}
}
return t.router.PreMatch(adapter.InboundContext{
Inbound: t.Tag(),
InboundType: t.Type(),
Network: network,
Source: source,
Destination: destination,
})
}
func (t *Endpoint) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
var metadata adapter.InboundContext
metadata.Inbound = t.Tag()
metadata.InboundType = t.Type()
metadata.Source = source
addr4, addr6 := t.server.TailscaleIPs()
switch destination.Addr {
case addr4:
destination.Addr = netip.AddrFrom4([4]uint8{127, 0, 0, 1})
case addr6:
destination.Addr = netip.IPv6Loopback()
}
metadata.Destination = destination
t.logger.InfoContext(ctx, "inbound connection from ", source)
t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
t.router.RouteConnectionEx(ctx, conn, metadata, onClose)
}
func (t *Endpoint) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
var metadata adapter.InboundContext
metadata.Inbound = t.Tag()
metadata.InboundType = t.Type()
metadata.Source = source
metadata.Destination = destination
addr4, addr6 := t.server.TailscaleIPs()
switch destination.Addr {
case addr4:
metadata.OriginDestination = destination
destination.Addr = netip.AddrFrom4([4]uint8{127, 0, 0, 1})
conn = bufio.NewNATPacketConn(bufio.NewNetPacketConn(conn), metadata.OriginDestination, metadata.Destination)
case addr6:
metadata.OriginDestination = destination
destination.Addr = netip.IPv6Loopback()
conn = bufio.NewNATPacketConn(bufio.NewNetPacketConn(conn), metadata.OriginDestination, metadata.Destination)
}
t.logger.InfoContext(ctx, "inbound packet connection from ", source)
t.logger.InfoContext(ctx, "inbound packet connection to ", destination)
t.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
}
func addressFromAddr(destination netip.Addr) tcpip.Address {
if destination.Is6() {
return tcpip.AddrFrom16(destination.As16())
} else {
return tcpip.AddrFrom4(destination.As4())
}
}

View File

@@ -1,16 +0,0 @@
package tailscale
import (
"github.com/sagernet/sing-box/experimental/libbox/platform"
"github.com/sagernet/tailscale/net/netns"
)
func setAndroidProtectFunc(platformInterface platform.Interface) {
if platformInterface != nil {
netns.SetAndroidProtectFunc(func(fd int) error {
return platformInterface.AutoDetectInterfaceControl(fd)
})
} else {
netns.SetAndroidProtectFunc(nil)
}
}

View File

@@ -1,8 +0,0 @@
//go:build !android
package tailscale
import "github.com/sagernet/sing-box/experimental/libbox/platform"
func setAndroidProtectFunc(platformInterface platform.Interface) {
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer"
"github.com/sagernet/sing-box/common/tlsfragment"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/bufio"
@@ -75,6 +76,21 @@ func (m *ConnectionManager) NewConnection(ctx context.Context, this N.Dialer, co
m.logger.ErrorContext(ctx, err)
return
}
if metadata.TLSFragment {
fallbackDelay := metadata.TLSFragmentFallbackDelay
if fallbackDelay == 0 {
fallbackDelay = C.TLSFragmentFallbackDelay
}
var newConn *tf.Conn
newConn, err = tf.NewConn(remoteConn, ctx, fallbackDelay)
if err != nil {
conn.Close()
remoteConn.Close()
m.logger.ErrorContext(ctx, err)
return
}
remoteConn = newConn
}
m.access.Lock()
element := m.connections.PushBack(conn)
m.access.Unlock()

View File

@@ -32,7 +32,18 @@ import (
// Deprecated: use RouteConnectionEx instead.
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return r.routeConnection(ctx, conn, metadata, nil)
done := make(chan interface{})
err := r.routeConnection(ctx, conn, metadata, N.OnceClose(func(it error) {
close(done)
}))
if err != nil {
return err
}
select {
case <-done:
case <-r.ctx.Done():
}
return nil
}
func (r *Router) RouteConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
@@ -140,7 +151,10 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
}
func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
err := r.routePacketConnection(ctx, conn, metadata, nil)
done := make(chan interface{})
err := r.routePacketConnection(ctx, conn, metadata, N.OnceClose(func(it error) {
close(done)
}))
if err != nil {
conn.Close()
if E.IsClosedOrCanceled(err) {
@@ -149,6 +163,10 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
r.logger.ErrorContext(ctx, err)
}
}
select {
case <-done:
case <-r.ctx.Done():
}
return nil
}
@@ -436,6 +454,10 @@ match:
if routeOptions.UDPTimeout > 0 {
metadata.UDPTimeout = routeOptions.UDPTimeout
}
if routeOptions.TLSFragment {
metadata.TLSFragment = true
metadata.TLSFragmentFallbackDelay = routeOptions.TLSFragmentFallbackDelay
}
}
switch action := currentRule.Action().(type) {
case *rule.RuleActionSniff:

View File

@@ -36,6 +36,8 @@ func NewRuleAction(ctx context.Context, logger logger.ContextLogger, action opti
FallbackDelay: time.Duration(action.RouteOptions.FallbackDelay),
UDPDisableDomainUnmapping: action.RouteOptions.UDPDisableDomainUnmapping,
UDPConnect: action.RouteOptions.UDPConnect,
TLSFragment: action.RouteOptions.TLSFragment,
TLSFragmentFallbackDelay: time.Duration(action.RouteOptions.TLSFragmentFallbackDelay),
},
}, nil
case C.RuleActionTypeRouteOptions:
@@ -47,6 +49,8 @@ func NewRuleAction(ctx context.Context, logger logger.ContextLogger, action opti
UDPDisableDomainUnmapping: action.RouteOptionsOptions.UDPDisableDomainUnmapping,
UDPConnect: action.RouteOptionsOptions.UDPConnect,
UDPTimeout: time.Duration(action.RouteOptionsOptions.UDPTimeout),
TLSFragment: action.RouteOptionsOptions.TLSFragment,
TLSFragmentFallbackDelay: time.Duration(action.RouteOptionsOptions.TLSFragmentFallbackDelay),
}, nil
case C.RuleActionTypeDirect:
directDialer, err := dialer.New(ctx, option.DialerOptions(action.DirectOptions), false)
@@ -100,6 +104,7 @@ func NewDNSRuleAction(logger logger.ContextLogger, action option.DNSRuleAction)
return &RuleActionDNSRoute{
Server: action.RouteOptions.Server,
RuleActionDNSRouteOptions: RuleActionDNSRouteOptions{
Strategy: C.DomainStrategy(action.RouteOptions.Strategy),
DisableCache: action.RouteOptions.DisableCache,
RewriteTTL: action.RouteOptions.RewriteTTL,
ClientSubnet: netip.Prefix(common.PtrValueOrDefault(action.RouteOptions.ClientSubnet)),
@@ -107,6 +112,7 @@ func NewDNSRuleAction(logger logger.ContextLogger, action option.DNSRuleAction)
}
case C.RuleActionTypeRouteOptions:
return &RuleActionDNSRouteOptions{
Strategy: C.DomainStrategy(action.RouteOptionsOptions.Strategy),
DisableCache: action.RouteOptionsOptions.DisableCache,
RewriteTTL: action.RouteOptionsOptions.RewriteTTL,
ClientSubnet: netip.Prefix(common.PtrValueOrDefault(action.RouteOptionsOptions.ClientSubnet)),
@@ -140,6 +146,9 @@ func (r *RuleActionRoute) String() string {
if r.UDPConnect {
descriptions = append(descriptions, "udp-connect")
}
if r.TLSFragment {
descriptions = append(descriptions, "tls-fragment")
}
return F.ToString("route(", strings.Join(descriptions, ","), ")")
}
@@ -153,6 +162,8 @@ type RuleActionRouteOptions struct {
UDPDisableDomainUnmapping bool
UDPConnect bool
UDPTimeout time.Duration
TLSFragment bool
TLSFragmentFallbackDelay time.Duration
}
func (r *RuleActionRouteOptions) Type() string {
@@ -167,6 +178,9 @@ func (r *RuleActionRouteOptions) String() string {
if r.UDPConnect {
descriptions = append(descriptions, "udp-connect")
}
if r.UDPTimeout > 0 {
descriptions = append(descriptions, "udp-timeout")
}
return F.ToString("route-options(", strings.Join(descriptions, ","), ")")
}
@@ -195,6 +209,7 @@ func (r *RuleActionDNSRoute) String() string {
}
type RuleActionDNSRouteOptions struct {
Strategy C.DomainStrategy
DisableCache bool
RewriteTTL *uint32
ClientSubnet netip.Prefix

View File

@@ -145,6 +145,11 @@ func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options op
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
rule.allItems = append(rule.allItems, item)
}
if options.IPAcceptAny {
item := NewIPAcceptAnyItem()
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
rule.allItems = append(rule.allItems, item)
}
if len(options.SourcePort) > 0 {
item := NewPortItem(true, options.SourcePort)
rule.sourcePortItems = append(rule.sourcePortItems, item)

View File

@@ -0,0 +1,21 @@
package rule
import (
"github.com/sagernet/sing-box/adapter"
)
var _ RuleItem = (*IPAcceptAnyItem)(nil)
type IPAcceptAnyItem struct{}
func NewIPAcceptAnyItem() *IPAcceptAnyItem {
return &IPAcceptAnyItem{}
}
func (r *IPAcceptAnyItem) Match(metadata *adapter.InboundContext) bool {
return len(metadata.DestinationAddresses) > 0
}
func (r *IPAcceptAnyItem) String() string {
return "ip_accept_any=true"
}

View File

@@ -59,7 +59,7 @@ func hasHeadlessRule(rules []option.HeadlessRule, cond func(rule option.DefaultH
}
func isProcessHeadlessRule(rule option.DefaultHeadlessRule) bool {
return len(rule.ProcessName) > 0 || len(rule.ProcessPath) > 0 || len(rule.PackageName) > 0
return len(rule.ProcessName) > 0 || len(rule.ProcessPath) > 0 || len(rule.ProcessPathRegex) > 0 || len(rule.PackageName) > 0
}
func isWIFIHeadlessRule(rule option.DefaultHeadlessRule) bool {

View File

@@ -45,10 +45,6 @@ func isProcessDNSRule(rule option.DefaultDNSRule) bool {
return len(rule.ProcessName) > 0 || len(rule.ProcessPath) > 0 || len(rule.ProcessPathRegex) > 0 || len(rule.PackageName) > 0 || len(rule.User) > 0 || len(rule.UserID) > 0
}
func isProcessHeadlessRule(rule option.DefaultHeadlessRule) bool {
return len(rule.ProcessName) > 0 || len(rule.ProcessPath) > 0 || len(rule.ProcessPathRegex) > 0 || len(rule.PackageName) > 0
}
func isWIFIRule(rule option.DefaultRule) bool {
return len(rule.WIFISSID) > 0 || len(rule.WIFIBSSID) > 0
}

View File

@@ -82,6 +82,10 @@ func (ho *HTTPObfs) Write(b []byte) (int, error) {
return ho.Conn.Write(b)
}
func (ho *HTTPObfs) Upstream() any {
return ho.Conn
}
// NewHTTPObfs return a HTTPObfs
func NewHTTPObfs(conn net.Conn, host string, port string) net.Conn {
return &HTTPObfs{

View File

@@ -113,6 +113,10 @@ func (to *TLSObfs) write(b []byte) (int, error) {
return len(b), err
}
func (to *TLSObfs) Upstream() any {
return to.Conn
}
// NewTLSObfs return a SimpleObfs
func NewTLSObfs(conn net.Conn, server string) net.Conn {
return &TLSObfs{