Compare commits

...

7 Commits

Author SHA1 Message Date
世界
19d08b55c8 Update documentation 2023-02-08 17:35:50 +08:00
世界
39514b3ca0 Add v2ray user stats api 2023-02-08 16:50:15 +08:00
世界
7ea9d48987 Add DHCP DNS server support 2023-02-08 16:43:29 +08:00
lyc8503
df3a982141 Add SSH outbound host key validation 2023-02-08 16:20:48 +08:00
世界
687b4509df Add query_type DNS rule item 2023-02-08 16:18:40 +08:00
世界
41ec2e7944 Add support for clash DNS query API 2023-02-08 14:13:33 +08:00
世界
1bd3a9144d Upgrade tfo-go 2023-02-08 14:13:33 +08:00
35 changed files with 746 additions and 103 deletions

View File

@@ -45,6 +45,6 @@ type V2RayServer interface {
}
type V2RayStatsService interface {
RoutedConnection(inbound string, outbound string, conn net.Conn) net.Conn
RoutedPacketConnection(inbound string, outbound string, conn N.PacketConn) N.PacketConn
RoutedConnection(inbound string, outbound string, user string, conn net.Conn) net.Conn
RoutedPacketConnection(inbound string, outbound string, user string, conn N.PacketConn) N.PacketConn
}

View File

@@ -46,6 +46,10 @@ type InboundContext struct {
SourceGeoIPCode string
GeoIPCode string
ProcessInfo *process.Info
// dns cache
QueryType uint16
}
type inboundContextKey struct{}

View File

@@ -47,6 +47,20 @@ type Router interface {
SetV2RayServer(server V2RayServer)
}
type routerContextKey struct{}
func ContextWithRouter(ctx context.Context, router Router) context.Context {
return context.WithValue(ctx, (*routerContextKey)(nil), router)
}
func RouterFromContext(ctx context.Context) Router {
metadata := ctx.Value((*routerContextKey)(nil))
if metadata == nil {
return nil
}
return metadata.(Router)
}
type Rule interface {
Service
Type() string

View File

@@ -13,8 +13,7 @@ import (
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/database64128/tfo-go/v2"
"github.com/sagernet/tfo-go"
)
var warnBindInterfaceOnUnsupportedPlatform = warning.New(

View File

@@ -12,8 +12,7 @@ import (
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/database64128/tfo-go/v2"
"github.com/sagernet/tfo-go"
)
type slowOpenConn struct {

8
constant/dhcp.go Normal file
View File

@@ -0,0 +1,8 @@
package constant
import "time"
const (
DHCPTTL = time.Hour
DHCPTimeout = time.Minute
)

View File

@@ -1,3 +1,14 @@
#### 1.2-beta1
* Add [DHCP DNS server](/configuration/dns/server) support
* Add SSH [host key validation](/configuration/outbound/ssh) support
* Add [query_type](/configuration/dns/rule) DNS rule item
* Add v2ray [user stats](/configuration/experimental#statsusers) api
* Add new clash DNS query api
* Improve vmess request
* Fix ipv6 redirect on Linux
* Fix match geoip private
#### 1.1.5
* Add Go 1.20 support

View File

@@ -9,6 +9,11 @@
"mixed-in"
],
"ip_version": 6,
"query_type": [
"A",
"HTTPS",
32768
],
"network": "tcp",
"auth_user": [
"usera",
@@ -119,6 +124,10 @@ Tags of [Inbound](/configuration/inbound).
Not limited if empty.
#### query_type
DNS query type. Values can be integers or type name strings.
#### network
`tcp` or `udp`.

View File

@@ -9,6 +9,11 @@
"mixed-in"
],
"ip_version": 6,
"query_type": [
"A",
"HTTPS",
32768
],
"network": "tcp",
"auth_user": [
"usera",
@@ -118,6 +123,10 @@
默认不限制。
#### query_type
DNS 查询类型。值可以为整数或者类型名称字符串。
#### network
`tcp``udp`

View File

@@ -30,16 +30,17 @@ The tag of the dns server.
The address of the dns server.
| Protocol | Format |
|----------|-----------------------------|
| `System` | `local` |
| `TCP` | `tcp://1.0.0.1` |
| `UDP` | `8.8.8.8` `udp://8.8.4.4` |
| `TLS` | `tls://dns.google` |
| `HTTPS` | `https://1.1.1.1/dns-query` |
| `QUIC` | `quic://dns.adguard.com` |
| `HTTP3` | `h3://8.8.8.8/dns-query` |
| `RCode` | `rcode://refused` |
| Protocol | Format |
|----------|-------------------------------|
| `System` | `local` |
| `TCP` | `tcp://1.0.0.1` |
| `UDP` | `8.8.8.8` `udp://8.8.4.4` |
| `TLS` | `tls://dns.google` |
| `HTTPS` | `https://1.1.1.1/dns-query` |
| `QUIC` | `quic://dns.adguard.com` |
| `HTTP3` | `h3://8.8.8.8/dns-query` |
| `RCode` | `rcode://refused` |
| `DHCP` | `dhcp://auto` or `dhcp://en0` |
!!! warning ""
@@ -53,6 +54,10 @@ The address of the dns server.
the RCode transport is often used to block queries. Use with rules and the `disable_cache` rule option.
!!! warning ""
DHCP transport is not included by default, see [Installation](/#installation).
| RCode | Description |
|-------------------|-----------------------|
| `success` | `No error` |

View File

@@ -30,16 +30,17 @@ DNS 服务器的标签。
DNS 服务器的地址。
| 协议 | 格式 |
|----------|-----------------------------|
| `System` | `local` |
| `TCP` | `tcp://1.0.0.1` |
| `UDP` | `8.8.8.8` `udp://8.8.4.4` |
| `TLS` | `tls://dns.google` |
| `HTTPS` | `https://1.1.1.1/dns-query` |
| `QUIC` | `quic://dns.adguard.com` |
| `HTTP3` | `h3://8.8.8.8/dns-query` |
| `RCode` | `rcode://refused` |
| 协议 | 格式 |
|----------|------------------------------|
| `System` | `local` |
| `TCP` | `tcp://1.0.0.1` |
| `UDP` | `8.8.8.8` `udp://8.8.4.4` |
| `TLS` | `tls://dns.google` |
| `HTTPS` | `https://1.1.1.1/dns-query` |
| `QUIC` | `quic://dns.adguard.com` |
| `HTTP3` | `h3://8.8.8.8/dns-query` |
| `RCode` | `rcode://refused` |
| `DHCP` | `dhcp://auto``dhcp://en0` |
!!! warning ""
@@ -53,6 +54,10 @@ DNS 服务器的地址。
RCode 传输层传输层常用于屏蔽请求. 与 DNS 规则和 `disable_cache` 规则选项一起使用。
!!! warning ""
默认安装不包含 DHCP 传输层,请参阅 [安装](/zh/#_2)。
| RCode | 描述 |
|-------------------|----------|
| `success` | `无错误` |

View File

@@ -9,7 +9,6 @@
"external_controller": "127.0.0.1:9090",
"external_ui": "folder",
"secret": "",
"direct_io": false,
"default_mode": "rule",
"store_selected": false,
"cache_file": "cache.db"
@@ -18,13 +17,15 @@
"listen": "127.0.0.1:8080",
"stats": {
"enabled": true,
"direct_io": false,
"inbounds": [
"socks-in"
],
"outbounds": [
"proxy",
"direct"
],
"users": [
"sekai"
]
}
}
@@ -58,10 +59,6 @@ Secret for the RESTful API (optional)
Authenticate by spedifying HTTP header `Authorization: Bearer ${secret}`
ALWAYS set a secret if RESTful API is listening on 0.0.0.0
#### direct_io
Allows lossless relays like splice without real-time traffic reporting.
#### default_mode
Default mode in clash, `rule` will be used if empty.
@@ -98,10 +95,6 @@ Traffic statistics service settings.
Enable statistics service.
#### stats.direct_io
Allows lossless relays like splice without real-time traffic reporting.
#### stats.inbounds
Inbound list to count traffic.
@@ -109,3 +102,7 @@ Inbound list to count traffic.
#### stats.outbounds
Outbound list to count traffic.
#### stats.users
User list to count traffic.

View File

@@ -9,7 +9,6 @@
"external_controller": "127.0.0.1:9090",
"external_ui": "folder",
"secret": "",
"direct_io": false,
"default_mode": "rule",
"store_selected": false,
"cache_file": "cache.db"
@@ -18,13 +17,15 @@
"listen": "127.0.0.1:8080",
"stats": {
"enabled": true,
"direct_io": false,
"inbounds": [
"socks-in"
],
"outbounds": [
"proxy",
"direct"
],
"users": [
"sekai"
]
}
}
@@ -56,10 +57,6 @@ RESTful API 的密钥(可选)
通过指定 HTTP 标头 `Authorization: Bearer ${secret}` 进行身份验证
如果 RESTful API 正在监听 0.0.0.0,请始终设置一个密钥。
#### direct_io
允许像 splice 这样的没有实时流量报告的无损中继。
#### default_mode
Clash 中的默认模式,默认使用 `rule`
@@ -96,10 +93,6 @@ gRPC API 监听地址。如果为空,则禁用 V2Ray API。
启用统计服务。
#### stats.direct_io
允许像 splice 这样的没有实时流量报告的无损中继。
#### stats.inbounds
统计流量的入站列表。
@@ -107,3 +100,7 @@ gRPC API 监听地址。如果为空,则禁用 V2Ray API。
#### stats.outbounds
统计流量的出站列表。
#### stats.users
统计流量的用户列表。

View File

@@ -12,6 +12,9 @@
"private_key": "",
"private_key_path": "$HOME/.ssh/id_rsa",
"private_key_passphrase": "",
"host_key": [
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdH..."
],
"host_key_algorithms": [],
"client_version": "SSH-2.0-OpenSSH_7.4p1",
@@ -51,6 +54,10 @@ Private key path.
Private key passphrase.
#### host_key
Host key. Accept any if empty.
#### host_key_algorithms
Host key algorithms.

View File

@@ -12,6 +12,9 @@
"private_key": "",
"private_key_path": "$HOME/.ssh/id_rsa",
"private_key_passphrase": "",
"host_key": [
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdH..."
],
"host_key_algorithms": [],
"client_version": "SSH-2.0-OpenSSH_7.4p1",
@@ -51,6 +54,10 @@ SSH 用户, 默认使用 root。
密钥密码。
#### host_key
主机密钥,留空接受所有。
#### host_key_algorithms
主机密钥算法。

View File

@@ -24,8 +24,9 @@ go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@lat
| Build Tag | Description |
|------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `with_quic` | Build with QUIC support, see [QUIC and HTTP3 dns transports](./configuration/dns/server), [Naive inbound](./configuration/inbound/naive), [Hysteria Inbound](./configuration/inbound/hysteria), [Hysteria Outbound](./configuration/outbound/hysteria) and [V2Ray Transport#QUIC](./configuration/shared/v2ray-transport#quic). |
| `with_quic` | Build with QUIC support, see [QUIC and HTTP3 DNS transports](./configuration/dns/server), [Naive inbound](./configuration/inbound/naive), [Hysteria Inbound](./configuration/inbound/hysteria), [Hysteria Outbound](./configuration/outbound/hysteria) and [V2Ray Transport#QUIC](./configuration/shared/v2ray-transport#quic). |
| `with_grpc` | Build with standard gRPC support, see [V2Ray Transport#gRPC](./configuration/shared/v2ray-transport#grpc). |
| `with_dhcp` | Build with DHCP support, see [DHCP DNS transport](./configuration/dns/server). |
| `with_wireguard` | Build with WireGuard support, see [WireGuard outbound](./configuration/outbound/wireguard). |
| `with_shadowsocksr` | Build with ShadowsocksR support, see [ShadowsocksR outbound](./configuration/outbound/shadowsocksr). |
| `with_ech` | Build with TLS ECH extension support for TLS outbound, see [TLS](./configuration/shared/tls#ech). |

View File

@@ -26,6 +26,7 @@ go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@lat
|------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `with_quic` | 启用 QUIC 支持,参阅 [QUIC 和 HTTP3 DNS 传输层](./configuration/dns/server)[Naive 入站](./configuration/inbound/naive)[Hysteria 入站](./configuration/inbound/hysteria)[Hysteria 出站](./configuration/outbound/hysteria) 和 [V2Ray 传输层#QUIC](./configuration/shared/v2ray-transport#quic)。 |
| `with_grpc` | 启用标准 gRPC 支持,参阅 [V2Ray 传输层#gRPC](./configuration/shared/v2ray-transport#grpc)。 |
| `with_dhcp` | 启用 DHCP 支持,参阅 [DHCP DNS 传输层](./configuration/dns/server)。 |
| `with_wireguard` | 启用 WireGuard 支持,参阅 [WireGuard 出站](./configuration/outbound/wireguard)。 |
| `with_shadowsocksr` | 启用 ShadowsocksR 支持,参阅 [ShadowsocksR 出站](./configuration/outbound/shadowsocksr)。 |
| `with_ech` | 启用 TLS ECH 扩展支持,参阅 [TLS](./configuration/shared/tls#ech)。 |

View File

@@ -0,0 +1,82 @@
package clashapi
import (
"context"
"net/http"
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing/common"
"github.com/go-chi/chi/v5"
"github.com/go-chi/render"
"github.com/miekg/dns"
)
func dnsRouter(router adapter.Router) http.Handler {
r := chi.NewRouter()
r.Get("/query", queryDNS(router))
return r
}
func queryDNS(router adapter.Router) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
qTypeStr := r.URL.Query().Get("type")
if qTypeStr == "" {
qTypeStr = "A"
}
qType, exist := dns.StringToType[qTypeStr]
if !exist {
render.Status(r, http.StatusBadRequest)
render.JSON(w, r, newError("invalid query type"))
return
}
ctx, cancel := context.WithTimeout(context.Background(), C.DNSTimeout)
defer cancel()
msg := dns.Msg{}
msg.SetQuestion(dns.Fqdn(name), qType)
resp, err := router.Exchange(ctx, &msg)
if err != nil {
render.Status(r, http.StatusInternalServerError)
render.JSON(w, r, newError(err.Error()))
return
}
responseData := render.M{
"Status": resp.Rcode,
"Question": resp.Question,
"Server": "internal",
"TC": resp.Truncated,
"RD": resp.RecursionDesired,
"RA": resp.RecursionAvailable,
"AD": resp.AuthenticatedData,
"CD": resp.CheckingDisabled,
}
rr2Json := func(rr dns.RR) render.M {
header := rr.Header()
return render.M{
"name": header.Name,
"type": header.Rrtype,
"TTL": header.Ttl,
"data": rr.String()[len(header.String()):],
}
}
if len(resp.Answer) > 0 {
responseData["Answer"] = common.Map(resp.Answer, rr2Json)
}
if len(resp.Ns) > 0 {
responseData["Authority"] = common.Map(resp.Ns, rr2Json)
}
if len(resp.Extra) > 0 {
responseData["Additional"] = common.Map(resp.Extra, rr2Json)
}
render.JSON(w, r, responseData)
}
}

View File

@@ -99,6 +99,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
r.Mount("/script", scriptRouter())
r.Mount("/profile", profileRouter())
r.Mount("/cache", cacheRouter())
r.Mount("/dns", dnsRouter(router))
})
if options.ExternalUI != "" {
chiRouter.Group(func(r chi.Router) {

View File

@@ -31,6 +31,7 @@ type StatsService struct {
createdAt time.Time
inbounds map[string]bool
outbounds map[string]bool
users map[string]bool
access sync.Mutex
counters map[string]*atomic.Int64
}
@@ -41,26 +42,32 @@ func NewStatsService(options option.V2RayStatsServiceOptions) *StatsService {
}
inbounds := make(map[string]bool)
outbounds := make(map[string]bool)
users := make(map[string]bool)
for _, inbound := range options.Inbounds {
inbounds[inbound] = true
}
for _, outbound := range options.Outbounds {
outbounds[outbound] = true
}
for _, user := range options.Users {
users[user] = true
}
return &StatsService{
createdAt: time.Now(),
inbounds: inbounds,
outbounds: outbounds,
users: users,
counters: make(map[string]*atomic.Int64),
}
}
func (s *StatsService) RoutedConnection(inbound string, outbound string, conn net.Conn) net.Conn {
func (s *StatsService) RoutedConnection(inbound string, outbound string, user string, conn net.Conn) net.Conn {
var readCounter []*atomic.Int64
var writeCounter []*atomic.Int64
countInbound := inbound != "" && s.inbounds[inbound]
countOutbound := outbound != "" && s.outbounds[outbound]
if !countInbound && !countOutbound {
countUser := user != "" && s.users[user]
if !countInbound && !countOutbound && !countUser {
return conn
}
s.access.Lock()
@@ -72,16 +79,21 @@ func (s *StatsService) RoutedConnection(inbound string, outbound string, conn ne
readCounter = append(readCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>uplink"))
writeCounter = append(writeCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>downlink"))
}
if countUser {
readCounter = append(readCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>uplink"))
writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink"))
}
s.access.Unlock()
return trackerconn.New(conn, readCounter, writeCounter)
}
func (s *StatsService) RoutedPacketConnection(inbound string, outbound string, conn N.PacketConn) N.PacketConn {
func (s *StatsService) RoutedPacketConnection(inbound string, outbound string, user string, conn N.PacketConn) N.PacketConn {
var readCounter []*atomic.Int64
var writeCounter []*atomic.Int64
countInbound := inbound != "" && s.inbounds[inbound]
countOutbound := outbound != "" && s.outbounds[outbound]
if !countInbound && !countOutbound {
countUser := user != "" && s.users[user]
if !countInbound && !countOutbound && !countUser {
return conn
}
s.access.Lock()
@@ -93,6 +105,10 @@ func (s *StatsService) RoutedPacketConnection(inbound string, outbound string, c
readCounter = append(readCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>uplink"))
writeCounter = append(writeCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>downlink"))
}
if countUser {
readCounter = append(readCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>uplink"))
writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink"))
}
s.access.Unlock()
return trackerconn.NewPacket(conn, readCounter, writeCounter)
}

5
go.mod
View File

@@ -7,7 +7,6 @@ require (
github.com/Dreamacro/clash v1.13.0
github.com/caddyserver/certmagic v0.17.2
github.com/cretz/bine v0.2.0
github.com/database64128/tfo-go/v2 v2.0.2
github.com/dustin/go-humanize v1.0.1
github.com/fsnotify/fsnotify v1.6.0
github.com/go-chi/chi/v5 v5.0.8
@@ -15,6 +14,7 @@ require (
github.com/go-chi/render v1.0.2
github.com/gofrs/uuid v4.4.0+incompatible
github.com/hashicorp/yamux v0.1.1
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8
github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/mholt/acmez v1.0.4
github.com/miekg/dns v1.1.50
@@ -29,6 +29,7 @@ require (
github.com/sagernet/sing-tun v0.1.1
github.com/sagernet/sing-vmess v0.1.1-0.20230207064843-983dde690564
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c
github.com/spf13/cobra v1.6.1
@@ -59,6 +60,7 @@ require (
github.com/google/btree v1.0.1 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/compress v1.15.15 // indirect
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
github.com/libdns/libdns v0.2.1 // indirect
@@ -72,6 +74,7 @@ require (
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/mod v0.6.0 // indirect

54
go.sum
View File

@@ -19,13 +19,12 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
github.com/database64128/tfo-go/v2 v2.0.2 h1:5rGgkJeLEKlNaqredfrPQNLnctn1b+1fq/8tdKdOzJg=
github.com/database64128/tfo-go/v2 v2.0.2/go.mod h1:FDdt4JaAsRU66wsYHxSVytYimPkKIHupVsxM+5DhvjY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
@@ -45,15 +44,32 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 h1:Z72DOke2yOK0Ms4Z2LK1E1OrRJXOxSj5DllTz2FYTRg=
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8/go.mod h1:m5WMe03WCvWcXjRnhvaAbAAXdCnu20J5P+mmH44ZzpE=
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
@@ -67,6 +83,13 @@ github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
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/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/mholt/acmez v1.0.4 h1:N3cE4Pek+dSolbsofIkAYz6H1d3pE+2G0os7QHslf80=
github.com/mholt/acmez v1.0.4/go.mod h1:qFGLZ4u+ehWINeJZjzPlsnjJBCPAADWTcIqE/7DAYQY=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
@@ -117,10 +140,14 @@ github.com/sagernet/sing-vmess v0.1.1-0.20230207064843-983dde690564 h1:+CFee8wEc
github.com/sagernet/sing-vmess v0.1.1-0.20230207064843-983dde690564/go.mod h1:9KkmnQzTL4Gvv8U2TRAH2BOITCGsGPpHtUPP5sxn5sY=
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d h1:trP/l6ZPWvQ/5Gv99Z7/t/v8iYy06akDMejxW1sznUk=
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d/go.mod h1:jk6Ii8Y3En+j2KQDLgdgQGwb3M6y7EL567jFnGYhN9g=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@@ -130,11 +157,14 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
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.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f h1:dpx1PHxYqAnXzbryJrWP1NQLzEjwcVgFLhkknuFQ7ww=
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f/go.mod h1:IogEAUBXDEwX7oR/BMmCctShYs80ql4hF0ySdzGxf7E=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
@@ -155,6 +185,7 @@ go4.org/netipx v0.0.0-20230125063823-8449b0a6169f/go.mod h1:tgPU4N2u9RByaTN3NC2p
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
@@ -166,7 +197,14 @@ golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -178,10 +216,20 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -189,6 +237,7 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -207,6 +256,7 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqG
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=

View File

@@ -11,8 +11,7 @@ import (
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/database64128/tfo-go/v2"
"github.com/sagernet/tfo-go"
)
func (a *myInboundAdapter) ListenTCP() (net.Listener, error) {

5
include/dhcp.go Normal file
View File

@@ -0,0 +1,5 @@
//go:build with_dhcp
package include
import _ "github.com/sagernet/sing-box/transport/dhcp"

18
include/dhcp_stub.go Normal file
View File

@@ -0,0 +1,18 @@
//go:build !with_dhcp
package include
import (
"context"
"github.com/sagernet/sing-dns"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
N "github.com/sagernet/sing/common/network"
)
func init() {
dns.RegisterTransport([]string{"dhcp"}, func(ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
return nil, E.New(`DHCP is not included in this build, rebuild with -tags with_dhcp`)
})
}

View File

@@ -77,32 +77,33 @@ func (r *DNSRule) UnmarshalJSON(bytes []byte) error {
}
type DefaultDNSRule struct {
Inbound Listable[string] `json:"inbound,omitempty"`
IPVersion int `json:"ip_version,omitempty"`
Network string `json:"network,omitempty"`
AuthUser Listable[string] `json:"auth_user,omitempty"`
Protocol Listable[string] `json:"protocol,omitempty"`
Domain Listable[string] `json:"domain,omitempty"`
DomainSuffix Listable[string] `json:"domain_suffix,omitempty"`
DomainKeyword Listable[string] `json:"domain_keyword,omitempty"`
DomainRegex Listable[string] `json:"domain_regex,omitempty"`
Geosite Listable[string] `json:"geosite,omitempty"`
SourceGeoIP Listable[string] `json:"source_geoip,omitempty"`
SourceIPCIDR Listable[string] `json:"source_ip_cidr,omitempty"`
SourcePort Listable[uint16] `json:"source_port,omitempty"`
SourcePortRange Listable[string] `json:"source_port_range,omitempty"`
Port Listable[uint16] `json:"port,omitempty"`
PortRange Listable[string] `json:"port_range,omitempty"`
ProcessName Listable[string] `json:"process_name,omitempty"`
ProcessPath Listable[string] `json:"process_path,omitempty"`
PackageName Listable[string] `json:"package_name,omitempty"`
User Listable[string] `json:"user,omitempty"`
UserID Listable[int32] `json:"user_id,omitempty"`
Outbound Listable[string] `json:"outbound,omitempty"`
ClashMode string `json:"clash_mode,omitempty"`
Invert bool `json:"invert,omitempty"`
Server string `json:"server,omitempty"`
DisableCache bool `json:"disable_cache,omitempty"`
Inbound Listable[string] `json:"inbound,omitempty"`
IPVersion int `json:"ip_version,omitempty"`
QueryType Listable[DNSQueryType] `json:"query_type,omitempty"`
Network string `json:"network,omitempty"`
AuthUser Listable[string] `json:"auth_user,omitempty"`
Protocol Listable[string] `json:"protocol,omitempty"`
Domain Listable[string] `json:"domain,omitempty"`
DomainSuffix Listable[string] `json:"domain_suffix,omitempty"`
DomainKeyword Listable[string] `json:"domain_keyword,omitempty"`
DomainRegex Listable[string] `json:"domain_regex,omitempty"`
Geosite Listable[string] `json:"geosite,omitempty"`
SourceGeoIP Listable[string] `json:"source_geoip,omitempty"`
SourceIPCIDR Listable[string] `json:"source_ip_cidr,omitempty"`
SourcePort Listable[uint16] `json:"source_port,omitempty"`
SourcePortRange Listable[string] `json:"source_port_range,omitempty"`
Port Listable[uint16] `json:"port,omitempty"`
PortRange Listable[string] `json:"port_range,omitempty"`
ProcessName Listable[string] `json:"process_name,omitempty"`
ProcessPath Listable[string] `json:"process_path,omitempty"`
PackageName Listable[string] `json:"package_name,omitempty"`
User Listable[string] `json:"user,omitempty"`
UserID Listable[int32] `json:"user_id,omitempty"`
Outbound Listable[string] `json:"outbound,omitempty"`
ClashMode string `json:"clash_mode,omitempty"`
Invert bool `json:"invert,omitempty"`
Server string `json:"server,omitempty"`
DisableCache bool `json:"disable_cache,omitempty"`
}
func (r DefaultDNSRule) IsValid() bool {

View File

@@ -8,6 +8,7 @@ type SSHOutboundOptions struct {
PrivateKey string `json:"private_key,omitempty"`
PrivateKeyPath string `json:"private_key_path,omitempty"`
PrivateKeyPassphrase string `json:"private_key_passphrase,omitempty"`
HostKey Listable[string] `json:"host_key,omitempty"`
HostKeyAlgorithms Listable[string] `json:"host_key_algorithms,omitempty"`
ClientVersion string `json:"client_version,omitempty"`
}

View File

@@ -8,7 +8,10 @@ import (
"github.com/sagernet/sing-box/common/json"
"github.com/sagernet/sing-dns"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
N "github.com/sagernet/sing/common/network"
mDNS "github.com/miekg/dns"
)
type ListenAddress netip.Addr
@@ -187,3 +190,40 @@ func (p *ListenPrefix) UnmarshalJSON(bytes []byte) error {
func (p ListenPrefix) Build() netip.Prefix {
return netip.Prefix(p)
}
type DNSQueryType uint16
func (t DNSQueryType) MarshalJSON() ([]byte, error) {
typeName, loaded := mDNS.TypeToString[uint16(t)]
if loaded {
return json.Marshal(typeName)
}
return json.Marshal(uint16(t))
}
func (t *DNSQueryType) UnmarshalJSON(bytes []byte) error {
var valueNumber uint16
err := json.Unmarshal(bytes, &valueNumber)
if err == nil {
*t = DNSQueryType(valueNumber)
return nil
}
var valueString string
err = json.Unmarshal(bytes, &valueString)
if err == nil {
queryType, loaded := mDNS.StringToType[valueString]
if loaded {
*t = DNSQueryType(queryType)
return nil
}
}
return E.New("unknown DNS query type: ", string(bytes))
}
func DNSQueryTypeToString(queryType uint16) string {
typeName, loaded := mDNS.TypeToString[queryType]
if loaded {
return typeName
}
return F.ToString(queryType)
}

View File

@@ -9,4 +9,5 @@ type V2RayStatsServiceOptions struct {
Enabled bool `json:"enabled,omitempty"`
Inbounds []string `json:"inbounds,omitempty"`
Outbounds []string `json:"outbounds,omitempty"`
Users []string `json:"users,omitempty"`
}

View File

@@ -1,7 +1,9 @@
package outbound
import (
"bytes"
"context"
"encoding/base64"
"math/rand"
"net"
"os"
@@ -32,6 +34,7 @@ type SSH struct {
dialer N.Dialer
serverAddr M.Socksaddr
user string
hostKey []ssh.PublicKey
hostKeyAlgorithms []string
clientVersion string
authMethod []ssh.AuthMethod
@@ -91,6 +94,15 @@ func NewSSH(ctx context.Context, router adapter.Router, logger log.ContextLogger
}
outbound.authMethod = append(outbound.authMethod, ssh.PublicKeys(signer))
}
if len(options.HostKey) > 0 {
for _, hostKey := range options.HostKey {
key, _, _, _, err := ssh.ParseAuthorizedKey([]byte(hostKey))
if err != nil {
return nil, E.New("parse host key ", key)
}
outbound.hostKey = append(outbound.hostKey, key)
}
}
return outbound, nil
}
@@ -126,7 +138,16 @@ func (s *SSH) connect() (*ssh.Client, error) {
ClientVersion: s.clientVersion,
HostKeyAlgorithms: s.hostKeyAlgorithms,
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
if len(s.hostKey) == 0 {
return nil
}
serverKey := key.Marshal()
for _, hostKey := range s.hostKey {
if bytes.Equal(serverKey, hostKey.Marshal()) {
return nil
}
}
return E.New("host key mismatch, server send ", key.Type(), " ", base64.StdEncoding.EncodeToString(serverKey))
},
}
clientConn, chans, reqs, err := ssh.NewClientConn(conn, s.serverAddr.Addr.String(), config)

View File

@@ -159,6 +159,7 @@ func NewRouter(ctx context.Context, logFactory log.Factory, options option.Route
transportTags[i] = tag
transportTagMap[tag] = true
}
ctx = adapter.ContextWithRouter(ctx, router)
for {
lastLen := len(dummyTransportMap)
for i, server := range dnsOptions.Servers {
@@ -173,7 +174,7 @@ func NewRouter(ctx context.Context, logFactory log.Factory, options option.Route
detour = dialer.NewDetour(router, server.Detour)
}
switch server.Address {
case "local", "rcode":
case "local":
default:
serverURL, _ := url.Parse(server.Address)
var serverAddress string
@@ -193,11 +194,15 @@ func NewRouter(ctx context.Context, logFactory log.Factory, options option.Route
} else {
continue
}
} else if notIpAddress != nil && (serverURL == nil || serverURL.Scheme != "rcode") {
return nil, E.New("parse dns server[", tag, "]: missing address_resolver")
} else if notIpAddress != nil {
switch serverURL.Scheme {
case "rcode", "dhcp":
default:
return nil, E.New("parse dns server[", tag, "]: missing address_resolver")
}
}
}
transport, err := dns.CreateTransport(ctx, logFactory.NewLogger(F.ToString("dns/transport[", i, "]")), detour, server.Address)
transport, err := dns.CreateTransport(ctx, logFactory.NewLogger(F.ToString("dns/transport[", tag, "]")), detour, server.Address)
if err != nil {
return nil, E.Cause(err, "parse dns server[", tag, "]")
}
@@ -392,14 +397,20 @@ func (r *Router) Start() error {
return err
}
}
for _, rule := range r.rules {
err := rule.Start()
if r.interfaceMonitor != nil {
err := r.interfaceMonitor.Start()
if err != nil {
return err
}
}
for _, rule := range r.dnsRules {
err := rule.Start()
if r.networkMonitor != nil {
err := r.networkMonitor.Start()
if err != nil {
return err
}
}
if r.packageManager != nil {
err := r.packageManager.Start()
if err != nil {
return err
}
@@ -424,22 +435,22 @@ func (r *Router) Start() error {
r.geositeCache = nil
r.geositeReader = nil
}
if r.interfaceMonitor != nil {
err := r.interfaceMonitor.Start()
for i, rule := range r.rules {
err := rule.Start()
if err != nil {
return err
return E.Cause(err, "initialize rule[", i, "]")
}
}
if r.networkMonitor != nil {
err := r.networkMonitor.Start()
for i, rule := range r.dnsRules {
err := rule.Start()
if err != nil {
return err
return E.Cause(err, "initialize DNS rule[", i, "]")
}
}
if r.packageManager != nil {
err := r.packageManager.Start()
for i, transport := range r.transports {
err := transport.Start()
if err != nil {
return err
return E.Cause(err, "initialize DNS server[", i, "]")
}
}
return nil
@@ -458,6 +469,12 @@ func (r *Router) Close() error {
return err
}
}
for _, transport := range r.transports {
err := transport.Close()
if err != nil {
return err
}
}
return common.Close(
common.PtrOrNil(r.geoIPReader),
r.interfaceMonitor,
@@ -583,7 +600,7 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
}
if r.v2rayServer != nil {
if statsService := r.v2rayServer.StatsService(); statsService != nil {
conn = statsService.RoutedConnection(metadata.Inbound, detour.Tag(), conn)
conn = statsService.RoutedConnection(metadata.Inbound, detour.Tag(), metadata.User, conn)
}
}
return detour.NewConnection(ctx, conn, metadata)
@@ -661,7 +678,7 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
}
if r.v2rayServer != nil {
if statsService := r.v2rayServer.StatsService(); statsService != nil {
conn = statsService.RoutedPacketConnection(metadata.Inbound, detour.Tag(), conn)
conn = statsService.RoutedPacketConnection(metadata.Inbound, detour.Tag(), metadata.User, conn)
}
}
return detour.NewPacketConnection(ctx, conn, metadata)

View File

@@ -50,7 +50,8 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
}
ctx, metadata := adapter.AppendContext(ctx)
if len(message.Question) > 0 {
switch message.Question[0].Qtype {
metadata.QueryType = message.Question[0].Qtype
switch metadata.QueryType {
case mDNS.TypeA:
metadata.IPVersion = 4
case mDNS.TypeAAAA:

View File

@@ -71,6 +71,11 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
return nil, E.New("invalid ip version: ", options.IPVersion)
}
}
if len(options.QueryType) > 0 {
item := NewQueryTypeItem(options.QueryType)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
if options.Network != "" {
switch options.Network {
case N.NetworkTCP, N.NetworkUDP:

47
route/rule_query_type.go Normal file
View File

@@ -0,0 +1,47 @@
package route
import (
"strings"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
)
var _ RuleItem = (*QueryTypeItem)(nil)
type QueryTypeItem struct {
typeList []uint16
typeMap map[uint16]bool
}
func NewQueryTypeItem(typeList []option.DNSQueryType) *QueryTypeItem {
rule := &QueryTypeItem{
typeList: common.Map(typeList, func(it option.DNSQueryType) uint16 {
return uint16(it)
}),
typeMap: make(map[uint16]bool),
}
for _, userId := range rule.typeList {
rule.typeMap[userId] = true
}
return rule
}
func (r *QueryTypeItem) Match(metadata *adapter.InboundContext) bool {
if metadata.QueryType == 0 {
return false
}
return r.typeMap[metadata.QueryType]
}
func (r *QueryTypeItem) String() string {
var description string
pLen := len(r.typeList)
if pLen == 1 {
description = "query_type=" + option.DNSQueryTypeToString(r.typeList[0])
} else {
description = "query_type=[" + strings.Join(common.Map(r.typeList, option.DNSQueryTypeToString), " ") + "]"
}
return description
}

262
transport/dhcp/server.go Normal file
View File

@@ -0,0 +1,262 @@
package dhcp
import (
"context"
"net"
"net/netip"
"net/url"
"os"
"strings"
"sync"
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-dns"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/control"
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/common/task"
"github.com/sagernet/sing/common/x/list"
"github.com/insomniacslk/dhcp/dhcpv4"
mDNS "github.com/miekg/dns"
)
func init() {
dns.RegisterTransport([]string{"dhcp"}, NewTransport)
}
type Transport struct {
ctx context.Context
router adapter.Router
logger logger.Logger
interfaceName string
autoInterface bool
interfaceCallback *list.Element[tun.DefaultInterfaceUpdateCallback]
transports []dns.Transport
updateAccess sync.Mutex
updatedAt time.Time
}
func NewTransport(ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
linkURL, err := url.Parse(link)
if err != nil {
return nil, err
}
if linkURL.Host == "" {
return nil, E.New("missing interface name for DHCP")
}
router := adapter.RouterFromContext(ctx)
if router == nil {
return nil, E.New("missing router in context")
}
transport := &Transport{
ctx: ctx,
router: router,
logger: logger,
interfaceName: linkURL.Host,
autoInterface: linkURL.Host == "auto",
}
return transport, nil
}
func (t *Transport) Start() error {
err := t.fetchServers()
if err != nil {
return err
}
if t.autoInterface {
t.interfaceCallback = t.router.InterfaceMonitor().RegisterCallback(t.interfaceUpdated)
}
return nil
}
func (t *Transport) Close() error {
if t.interfaceCallback != nil {
t.router.InterfaceMonitor().UnregisterCallback(t.interfaceCallback)
}
return nil
}
func (t *Transport) Raw() bool {
return true
}
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
err := t.fetchServers()
if err != nil {
return nil, err
}
if len(t.transports) == 0 {
return nil, E.New("dhcp: empty DNS servers from response")
}
var response *mDNS.Msg
for _, transport := range t.transports {
response, err = transport.Exchange(ctx, message)
if err == nil {
return response, nil
}
}
return nil, err
}
func (t *Transport) fetchInterface() (*net.Interface, error) {
interfaceName := t.interfaceName
if t.autoInterface {
if t.router.NetworkMonitor() == nil {
return nil, E.New("missing monitor for auto DHCP, set route.auto_detect_interface")
}
interfaceName = t.router.InterfaceMonitor().DefaultInterfaceName(netip.Addr{})
}
if interfaceName == "" {
return nil, E.New("missing default interface")
}
return net.InterfaceByName(interfaceName)
}
func (t *Transport) fetchServers() error {
if time.Since(t.updatedAt) < C.DHCPTTL {
return nil
}
t.updateAccess.Lock()
defer t.updateAccess.Unlock()
if time.Since(t.updatedAt) < C.DHCPTTL {
return nil
}
return t.updateServers()
}
func (t *Transport) updateServers() error {
iface, err := t.fetchInterface()
if err != nil {
return E.Cause(err, "dhcp: prepare interface")
}
t.logger.Info("dhcp: query DNS servers on ", iface.Name)
fetchCtx, cancel := context.WithTimeout(t.ctx, C.DHCPTimeout)
err = t.fetchServers0(fetchCtx, iface)
cancel()
if err != nil {
return err
} else if len(t.transports) == 0 {
return E.New("dhcp: empty DNS servers response")
} else {
t.updatedAt = time.Now()
return nil
}
}
func (t *Transport) interfaceUpdated(int) error {
return t.updateServers()
}
func (t *Transport) fetchServers0(ctx context.Context, iface *net.Interface) error {
var listener net.ListenConfig
listener.Control = control.Append(listener.Control, control.BindToInterfaceFunc(t.router.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int) {
return iface.Name, iface.Index
}))
listener.Control = control.Append(listener.Control, control.ReuseAddr())
packetConn, err := listener.ListenPacket(t.ctx, "udp4", "0.0.0.0:68")
if err != nil {
return err
}
defer packetConn.Close()
discovery, err := dhcpv4.NewDiscovery(iface.HardwareAddr, dhcpv4.WithBroadcast(true), dhcpv4.WithRequestedOptions(dhcpv4.OptionDomainNameServer))
if err != nil {
return err
}
_, err = packetConn.WriteTo(discovery.ToBytes(), &net.UDPAddr{IP: net.IPv4bcast, Port: 67})
if err != nil {
return err
}
var group task.Group
group.Append0(func(ctx context.Context) error {
return t.fetchServersResponse(iface, packetConn, discovery.TransactionID)
})
group.Cleanup(func() {
packetConn.Close()
})
return group.Run(ctx)
}
func (t *Transport) fetchServersResponse(iface *net.Interface, packetConn net.PacketConn, transactionID dhcpv4.TransactionID) error {
_buffer := buf.StackNewSize(dhcpv4.MaxMessageSize)
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
for {
_, _, err := buffer.ReadPacketFrom(packetConn)
if err != nil {
return err
}
dhcpPacket, err := dhcpv4.FromBytes(buffer.Bytes())
if err != nil {
t.logger.Trace("dhcp: parse DHCP response: ", err)
return err
}
if dhcpPacket.MessageType() != dhcpv4.MessageTypeOffer {
t.logger.Trace("dhcp: expected OFFER response, but got ", dhcpPacket.MessageType())
continue
}
if dhcpPacket.TransactionID != transactionID {
t.logger.Trace("dhcp: expected transaction ID ", transactionID, ", but got ", dhcpPacket.TransactionID)
continue
}
dns := dhcpPacket.DNS()
if len(dns) == 0 {
return nil
}
var addrs []netip.Addr
for _, ip := range dns {
addr, _ := netip.AddrFromSlice(ip)
addrs = append(addrs, addr.Unmap())
}
return t.recreateServers(iface, addrs)
}
}
func (t *Transport) recreateServers(iface *net.Interface, serverAddrs []netip.Addr) error {
if len(serverAddrs) > 0 {
t.logger.Info("dhcp: updated DNS servers from ", iface.Name, ": [", strings.Join(common.Map(serverAddrs, func(it netip.Addr) string {
return it.String()
}), ","), "]")
}
serverDialer := dialer.NewDefault(t.router, option.DialerOptions{
BindInterface: iface.Name,
UDPFragmentDefault: true,
})
var transports []dns.Transport
for _, serverAddr := range serverAddrs {
serverTransport, err := dns.NewUDPTransport(t.ctx, serverDialer, M.Socksaddr{Addr: serverAddr, Port: 53})
if err != nil {
return err
}
transports = append(transports, serverTransport)
}
t.transports = transports
return nil
}
func (t *Transport) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) {
return nil, os.ErrInvalid
}