1
0
mirror of https://github.com/MetaCubeX/mihomo.git synced 2026-10-10 12:13:10 +08:00

feat: add Tailscale outbound support (#2786)

This commit is contained in:
汐殇
2026-05-14 12:47:05 +08:00
committed by GitHub
parent 787dbfc5fd
commit 571925d2d2
15 changed files with 707 additions and 0 deletions
+475
View File
@@ -0,0 +1,475 @@
//go:build !no_tailscale
package outbound
import (
"context"
"errors"
"fmt"
"io"
"net"
"net/netip"
"sync"
"time"
N "github.com/metacubex/mihomo/common/net"
"github.com/metacubex/mihomo/component/ca"
"github.com/metacubex/mihomo/component/resolver"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/dns"
"github.com/metacubex/mihomo/log"
"github.com/metacubex/mihomo/tunnel"
"github.com/metacubex/tailscale/envknob"
"github.com/metacubex/tailscale/ipn"
"github.com/metacubex/tailscale/tsnet"
D "github.com/miekg/dns"
)
type Tailscale struct {
*Base
server *tsnet.Server
dnsResolver *dns.Resolver
option TailscaleOption
ctx context.Context
cancel context.CancelFunc
startOnce sync.Once
startErr error
backendInitOnce sync.Once
backendInitCh chan struct{}
backendInitErr error
startHook io.Closer
unregisterDNSResolver func()
}
type TailscaleOption struct {
BasicOption
Name string `proxy:"name"`
Hostname string `proxy:"hostname,omitempty"`
AuthKey string `proxy:"auth-key,omitempty"`
ControlURL string `proxy:"control-url,omitempty"`
StateDir string `proxy:"state-dir,omitempty"`
Ephemeral bool `proxy:"ephemeral,omitempty"`
UDP bool `proxy:"udp,omitempty"`
AcceptRoutes *bool `proxy:"accept-routes,omitempty"`
ExitNode string `proxy:"exit-node,omitempty"`
ExitNodeAllowLANAccess *bool `proxy:"exit-node-allow-lan-access,omitempty"`
}
func NewTailscale(option TailscaleOption) (*Tailscale, error) {
envknob.SetNoLogsNoSupport()
if _, err := buildTailscaleMaskedPrefs(option); err != nil {
return nil, err
}
if option.StateDir == "" {
option.StateDir = "tailscale"
}
option.StateDir = C.Path.Resolve(option.StateDir)
if !C.Path.IsSafePath(option.StateDir) {
return nil, C.Path.ErrNotSafePath(option.StateDir)
}
addr := option.ControlURL
if addr == "" {
addr = "tailscale"
}
ctx, cancel := context.WithCancel(context.Background())
outbound := &Tailscale{
Base: NewBase(BaseOption{
Name: option.Name,
Addr: addr,
Type: C.Tailscale,
ProviderName: option.ProviderName,
UDP: option.UDP,
Interface: option.Interface,
RoutingMark: option.RoutingMark,
Prefer: option.IPVersion,
}),
option: option,
ctx: ctx,
cancel: cancel,
backendInitCh: make(chan struct{}),
}
outbound.dialer = option.NewDialer(outbound.DialOptions())
outbound.server = &tsnet.Server{
Dir: option.StateDir,
Hostname: option.Hostname,
AuthKey: option.AuthKey,
ControlURL: option.ControlURL,
Ephemeral: option.Ephemeral,
SystemDialer: outbound.dialer.DialContext,
SystemPacketListener: tailscalePacketListener{dialer: outbound.dialer},
ExtraRootCAs: ca.GetCertPool(),
LookupHook: tailscaleLookupHook,
UserLogf: func(format string, args ...any) {
log.Infoln("[Tailscale](%s) %s", option.Name, fmt.Sprintf(format, args...))
},
Logf: func(format string, args ...any) {
log.Debugln("[Tailscale](%s) %s", option.Name, fmt.Sprintf(format, args...))
},
}
dnsTransport := tailscaleDNSTransport{tailscale: outbound}
outbound.dnsResolver = dns.NewResolverFromClient(dnsTransport)
outbound.unregisterDNSResolver = dns.RegisterTailscaleDnsClient(option.Name, dnsTransport)
outbound.startHook = tunnel.RegisterOnRunning(outbound.startOnRunning)
return outbound, nil
}
func (t *Tailscale) startOnRunning() {
if err := t.start(); err != nil {
log.Warnln("[Tailscale](%s) start failed: %v", t.Name(), err)
}
}
func (t *Tailscale) start() error {
t.startOnce.Do(func() {
if err := t.server.Start(); err != nil {
t.startErr = err
t.setBackendInitialized(err)
return
}
ctx, cancel := context.WithTimeout(t.ctx, 30*time.Second)
defer cancel()
if err := t.applyPrefs(ctx); err != nil {
t.startErr = err
t.setBackendInitialized(err)
return
}
go t.watchBackendState()
})
return t.startErr
}
func (t *Tailscale) ensureStarted(ctx context.Context) error {
if err := t.start(); err != nil {
return err
}
return t.waitBackendInitialized(ctx)
}
func (t *Tailscale) watchBackendState() {
lc, err := t.server.LocalClient()
if err != nil {
t.setBackendInitialized(err)
return
}
watcher, err := lc.WatchIPNBus(t.ctx, ipn.NotifyInitialState)
if err != nil {
t.setBackendInitialized(err)
return
}
defer watcher.Close()
backendInitialized := false
exitNodeNeedsStatus := tailscaleExitNodeNeedsStatus(t.option)
for {
n, err := watcher.Next()
if err != nil {
t.setBackendInitialized(err)
return
}
if n.State == nil {
continue
}
if *n.State != ipn.NoState && !backendInitialized {
t.setBackendInitialized(nil)
backendInitialized = true
if !exitNodeNeedsStatus {
return
}
}
if exitNodeNeedsStatus && *n.State == ipn.Running {
if err := t.applyExitNodePrefs(t.ctx); err != nil {
log.Warnln("[Tailscale](%s) set exit node failed: %v", t.Name(), err)
}
return
}
}
}
func (t *Tailscale) setBackendInitialized(err error) {
t.backendInitOnce.Do(func() {
t.backendInitErr = err
close(t.backendInitCh)
})
}
func (t *Tailscale) waitBackendInitialized(ctx context.Context) error {
select {
case <-t.backendInitCh:
return t.backendInitErr
case <-ctx.Done():
return ctx.Err()
case <-t.ctx.Done():
return t.ctx.Err()
}
}
func (t *Tailscale) applyPrefs(ctx context.Context) error {
mp, err := buildTailscaleMaskedPrefs(t.option)
if err != nil {
return err
}
if mp == nil {
return nil
}
lc, err := t.server.LocalClient()
if err != nil {
return err
}
_, err = lc.EditPrefs(ctx, mp)
return err
}
func (t *Tailscale) applyExitNodePrefs(ctx context.Context) error {
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
lc, err := t.server.LocalClient()
if err != nil {
return err
}
status, err := lc.Status(ctx)
if err != nil {
return err
}
mp := &ipn.MaskedPrefs{
ExitNodeIPSet: true,
}
if t.option.ExitNodeAllowLANAccess != nil {
mp.ExitNodeAllowLANAccess = *t.option.ExitNodeAllowLANAccess
mp.ExitNodeAllowLANAccessSet = true
}
if err = mp.SetExitNodeIP(t.option.ExitNode, status); err != nil {
return err
}
_, err = lc.EditPrefs(ctx, mp)
return err
}
func buildTailscaleMaskedPrefs(option TailscaleOption) (*ipn.MaskedPrefs, error) {
var mp ipn.MaskedPrefs
changed := false
if option.AcceptRoutes != nil {
mp.RouteAll = *option.AcceptRoutes
mp.RouteAllSet = true
changed = true
}
if option.ExitNode != "" {
if autoExitNode, ok := ipn.ParseAutoExitNodeString(option.ExitNode); ok {
mp.AutoExitNode = autoExitNode
mp.AutoExitNodeSet = true
changed = true
}
}
if option.ExitNodeAllowLANAccess != nil && !tailscaleExitNodeNeedsStatus(option) {
mp.ExitNodeAllowLANAccess = *option.ExitNodeAllowLANAccess
mp.ExitNodeAllowLANAccessSet = true
changed = true
}
if !changed {
return nil, nil
}
return &mp, nil
}
func tailscaleExitNodeNeedsStatus(option TailscaleOption) bool {
if option.ExitNode == "" {
return false
}
_, ok := ipn.ParseAutoExitNodeString(option.ExitNode)
return !ok
}
func tailscaleLookupHook(ctx context.Context, host string) ([]netip.Addr, error) {
return resolver.LookupIPWithResolver(ctx, host, resolver.ProxyServerHostResolver)
}
func (t *Tailscale) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) {
if err = t.ensureStarted(ctx); err != nil {
return nil, err
}
address := metadata.RemoteAddress()
if err = t.checkTailscaleRoute(ctx, "tcp", address); err != nil {
return nil, err
}
conn, err := t.server.Dial(ctx, "tcp", address)
if err != nil {
return nil, err
}
if conn == nil {
return nil, errors.New("conn is nil")
}
return NewConn(conn, t), nil
}
func (t *Tailscale) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {
if !t.option.UDP {
return nil, C.ErrNotSupport
}
if err = t.ensureStarted(ctx); err != nil {
return nil, err
}
if err = t.ResolveUDP(ctx, metadata); err != nil {
return nil, err
}
address := metadata.AddrPort().String()
if err = t.checkTailscaleRoute(ctx, "udp", address); err != nil {
return nil, err
}
conn, err := t.server.Dial(ctx, "udp", address)
if err != nil {
return nil, err
}
if conn == nil {
return nil, errors.New("packet conn is nil")
}
rAddr := metadata.UDPAddr()
if rAddr == nil {
return nil, errors.New("packet destination is invalid")
}
return newPacketConn(N.NewThreadSafePacketConn(&tailscaleConnPacketConn{Conn: conn, rAddr: rAddr}), t), nil
}
func (t *Tailscale) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {
if !metadata.Resolved() && metadata.Host != "" {
ip, err := t.resolveIPWithTransport(ctx, metadata.Host)
if err != nil {
return fmt.Errorf("can't resolve ip: %w", err)
}
metadata.DstIP = ip
}
return nil
}
func (t *Tailscale) checkTailscaleRoute(ctx context.Context, network, address string) error {
ipp, viaTailscale, err := t.server.DialPlan(ctx, network, address)
if err != nil {
return err
}
if !viaTailscale {
return fmt.Errorf("destination %s is not routed by Tailscale; configure exit-node or accept an advertised subnet route", ipp)
}
return nil
}
func (t *Tailscale) resolveIPWithTransport(ctx context.Context, host string) (netip.Addr, error) {
switch t.option.IPVersion {
case C.IPv4Only:
return resolver.ResolveIPv4WithResolver(ctx, host, t.dnsResolver)
case C.IPv6Only:
return resolver.ResolveIPv6WithResolver(ctx, host, t.dnsResolver)
case C.IPv6Prefer:
return resolver.ResolveIPPrefer6WithResolver(ctx, host, t.dnsResolver)
default:
return resolver.ResolveIPWithResolver(ctx, host, t.dnsResolver)
}
}
type tailscaleDNSTransport struct {
tailscale *Tailscale
}
func (t tailscaleDNSTransport) Address() string {
return "tailscale://" + t.tailscale.Name()
}
func (t tailscaleDNSTransport) ResetConnection() {}
func (t tailscaleDNSTransport) ExchangeContext(ctx context.Context, msg *D.Msg) (*D.Msg, error) {
if len(msg.Question) == 0 {
return nil, errors.New("should have one question at least")
}
if err := t.tailscale.ensureStarted(ctx); err != nil {
return nil, err
}
q := msg.Question[0]
qtypeName, ok := D.TypeToString[q.Qtype]
if !ok {
return nil, fmt.Errorf("unsupported query type: %d", q.Qtype)
}
lc, err := t.tailscale.server.LocalClient()
if err != nil {
return nil, err
}
response, _, err := lc.QueryDNS(ctx, q.Name, qtypeName)
if err != nil {
return nil, err
}
var responseMsg D.Msg
if err = responseMsg.Unpack(response); err != nil {
return nil, err
}
responseMsg.Id = msg.Id
return &responseMsg, nil
}
func (t *Tailscale) ProxyInfo() C.ProxyInfo {
info := t.Base.ProxyInfo()
info.DialerProxy = t.option.DialerProxy
return info
}
func (t *Tailscale) IsL3Protocol(metadata *C.Metadata) bool {
return true
}
func (t *Tailscale) Close() error {
t.cancel()
if t.startHook != nil {
_ = t.startHook.Close()
}
if t.unregisterDNSResolver != nil {
t.unregisterDNSResolver()
}
if t.server != nil {
return t.server.Close()
}
return nil
}
type tailscaleConnPacketConn struct {
net.Conn
rAddr net.Addr
}
type tailscalePacketListener struct {
dialer C.Dialer
}
func (l tailscalePacketListener) ListenPacket(ctx context.Context, network, address string) (net.PacketConn, error) {
return l.dialer.ListenPacket(ctx, network, address, netip.AddrPort{})
}
func (c *tailscaleConnPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
n, err := c.Conn.Read(b)
return n, c.rAddr, err
}
func (c *tailscaleConnPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
return c.Conn.Write(b)
}
func (c *tailscaleConnPacketConn) LocalAddr() net.Addr {
if addr := c.Conn.LocalAddr(); addr != nil {
return addr
}
return &net.UDPAddr{IP: net.IPv4zero, Port: 0}
}
func (c *tailscaleConnPacketConn) SetDeadline(t time.Time) error {
return c.Conn.SetDeadline(t)
}
func (c *tailscaleConnPacketConn) SetReadDeadline(t time.Time) error {
return c.Conn.SetReadDeadline(t)
}
func (c *tailscaleConnPacketConn) SetWriteDeadline(t time.Time) error {
return c.Conn.SetWriteDeadline(t)
}
+28
View File
@@ -0,0 +1,28 @@
//go:build no_tailscale
package outbound
import "fmt"
type Tailscale struct {
*Base
}
type TailscaleOption struct {
BasicOption
Name string `proxy:"name"`
Hostname string `proxy:"hostname,omitempty"`
AuthKey string `proxy:"auth-key,omitempty"`
ControlURL string `proxy:"control-url,omitempty"`
StateDir string `proxy:"state-dir,omitempty"`
Ephemeral bool `proxy:"ephemeral,omitempty"`
UDP bool `proxy:"udp,omitempty"`
AcceptRoutes *bool `proxy:"accept-routes,omitempty"`
ExitNode string `proxy:"exit-node,omitempty"`
ExitNodeAllowLANAccess *bool `proxy:"exit-node-allow-lan-access,omitempty"`
}
func NewTailscale(option TailscaleOption) (*Tailscale, error) {
return nil, fmt.Errorf("tailscale support is disabled by no_tailscale build tag")
}
+7
View File
@@ -180,6 +180,13 @@ func ParseProxy(mapping map[string]any, options ...ProxyOption) (C.Proxy, error)
break
}
proxy, err = outbound.NewOpenVPN(*openVPNOption)
case "tailscale":
tailscaleOption := &outbound.TailscaleOption{BasicOption: basicOption}
err = decoder.Decode(mapping, tailscaleOption)
if err != nil {
break
}
proxy, err = outbound.NewTailscale(*tailscaleOption)
default:
return nil, fmt.Errorf("unsupport proxy type: %s", proxyType)
}
+6
View File
@@ -1217,6 +1217,12 @@ func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns.
dnsNetType = "quic" // DNS over QUIC
case "system":
dnsNetType = "system" // System DNS
case "ts", "tailscale":
addr = u.Host
dnsNetType = "tailscale" // Tailscale DNS via proxy name
if addr == "" {
err = errors.New("missing Tailscale proxy name")
}
case "dhcp":
addr = server[len("dhcp://"):] // some special notation cannot be parsed by url
dnsNetType = "dhcp" // UDP from DHCP
+3
View File
@@ -48,6 +48,7 @@ const (
Masque
TrustTunnel
OpenVPN
Tailscale
)
const (
@@ -221,6 +222,8 @@ func (at AdapterType) String() string {
return "TrustTunnel"
case OpenVPN:
return "OpenVPN"
case Tailscale:
return "Tailscale"
case Relay:
return "Relay"
case Selector:
+5
View File
@@ -0,0 +1,5 @@
//go:build no_tailscale
package features
const NoTailscale = true
+5
View File
@@ -0,0 +1,5 @@
//go:build !no_tailscale
package features
const NoTailscale = false
+3
View File
@@ -10,6 +10,9 @@ func Tags() (tags []string) {
if NoFakeTCP {
tags = append(tags, "no_fake_tcp")
}
if NoTailscale {
tags = append(tags, "no_tailscale")
}
if WithGVisor {
tags = append(tags, "with_gvisor")
}
+8
View File
@@ -479,6 +479,14 @@ func (rs Resolvers) ResetConnection() {
rs.DirectResolver.ResetConnection()
}
func NewResolverFromClient(client dnsClient) *Resolver {
return &Resolver{
ipv6: true,
main: []dnsClient{client},
cache: Config{}.newCache(),
}
}
func NewResolver(config Config) (rs Resolvers) {
defaultResolver := &Resolver{
main: transform(config.Default, nil),
+65
View File
@@ -0,0 +1,65 @@
package dns
import (
"context"
"fmt"
"sync"
"sync/atomic"
D "github.com/miekg/dns"
)
type tailscaleDNSClient struct {
name string
}
type tailscaleResolverEntry struct {
id uint64
client dnsClient
}
var (
tailscaleResolverID atomic.Uint64
tailscaleResolverMu sync.RWMutex
tailscaleResolvers = map[string]tailscaleResolverEntry{}
)
var _ dnsClient = (*tailscaleDNSClient)(nil)
func RegisterTailscaleDnsClient(name string, client dnsClient) func() {
id := tailscaleResolverID.Add(1)
tailscaleResolverMu.Lock()
tailscaleResolvers[name] = tailscaleResolverEntry{
id: id,
client: client,
}
tailscaleResolverMu.Unlock()
return func() {
tailscaleResolverMu.Lock()
if entry, ok := tailscaleResolvers[name]; ok && entry.id == id {
delete(tailscaleResolvers, name)
}
tailscaleResolverMu.Unlock()
}
}
func newTailscaleClient(name string) *tailscaleDNSClient {
return &tailscaleDNSClient{name: name}
}
func (c *tailscaleDNSClient) Address() string {
return "tailscale://" + c.name
}
func (c *tailscaleDNSClient) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) {
tailscaleResolverMu.RLock()
entry, ok := tailscaleResolvers[c.name]
tailscaleResolverMu.RUnlock()
if !ok || entry.client == nil {
return nil, fmt.Errorf("proxy %q does not provide Tailscale DNS", c.name)
}
return entry.client.ExchangeContext(ctx, m)
}
func (c *tailscaleDNSClient) ResetConnection() {}
+2
View File
@@ -126,6 +126,8 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient {
c = newDHCPClient(s.Addr)
case "system":
c = newSystemClient()
case "tailscale":
c = newTailscaleClient(s.Addr)
case "rcode":
c = newRCodeClient(s.Addr)
case "quic":
+21
View File
@@ -310,6 +310,7 @@ dns:
- https://mozilla.cloudflare-dns.com/dns-query#DNS&h3=true # 指定策略组和使用 HTTP/3
- dhcp://en0 # dns from dhcp
- quic://dns.adguard.com:784 # DNS over QUIC
# - ts://tailscale # 使用指定 Tailscale 出站的 DNS 配置查询
# - '8.8.8.8#RULES' # 效果同respect-rules,但仅对该服务器生效
# - '8.8.8.8#en0' # 兼容指定 DNS 出口网卡
@@ -1087,6 +1088,26 @@ proxies: # socks5
# j3: <t><b 0xf6ab><c><r 10> # AmneziaWG v1.5 only (removed in v2)
# itime: 60 # AmneziaWG v1.5 only (removed in v2)
# tailscale
- name: "tailscale"
type: tailscale
# hostname: mihomo # Tailscale 设备名,默认由 tsnet 处理
# auth-key: tskey-auth-xxxx # 可选;不填写时首次启动会输出交互式登录 URL
# control-url: https://controlplane.tailscale.com # 可选;自定义 Headscale/Tailscale control server
# state-dir: ./tailscale # tsnet 状态目录,默认 tailscale
# ephemeral: false # 是否作为 ephemeral node 登录,默认 false
udp: true # 是否启用 UDP,默认 false
# accept-routes: true # 是否接受 Tailnet 中发布的 subnet routes
# exit-node: 100.64.0.1 # 使用指定 exit node,可填写节点 IP;也支持 auto:any
# exit-node-allow-lan-access: true # 使用 exit node 时是否允许访问本地 LAN
# 当目标不在 Tailscale 路由内时,连接会直接报错,不会回退到直连;
# 访问公网需要配置可用的 exit-node,或接受覆盖目标网段的 subnet routes。
# 一个出站代理的标识。当值不为空时,将使用指定的 proxy 发出 Tailscale 控制面和 DERP/STUN 等连接
# dialer-proxy: "ss1"
# interface-name: "WLAN" # 指定出站网卡
# routing-mark: 6666 # Linux 下配置 fwmark
# ip-version: ipv4-prefer # 可选:dual/ipv4/ipv6/ipv4-prefer/ipv6-prefer
# openvpn
- name: "openvpn"
type: openvpn
+22
View File
@@ -39,6 +39,7 @@ require (
github.com/metacubex/sing-wireguard v0.0.0-20260507084707-690d479ec947
github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141
github.com/metacubex/ssh v0.1.0
github.com/metacubex/tailscale v0.0.0-20260514010142-10fd27a2338f
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443
github.com/metacubex/tls v0.1.5
github.com/metacubex/utls v1.8.4
@@ -75,7 +76,10 @@ require (
github.com/RyuaNerin/go-krypto v1.3.0 // indirect
github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // 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.0.6 // indirect
github.com/coder/websocket v1.8.12 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dunglas/httpsfv v1.0.2 // indirect
github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358 // indirect
@@ -83,14 +87,19 @@ require (
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/gaukas/godicttls v0.0.4 // indirect
github.com/go-ole/go-ole v1.3.0 // 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-20241129210726-2c02b8208cf8 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
github.com/huin/goupnp v1.3.0 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/jsimonetti/rtnetlink v1.4.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/klauspost/reedsolomon v1.12.3 // indirect
github.com/kr/text v0.2.0 // indirect
@@ -99,21 +108,34 @@ require (
github.com/metacubex/gvisor v0.0.0-20251227095601-261ec1326fe8 // indirect
github.com/metacubex/hkdf v0.1.0 // indirect
github.com/metacubex/hpke v0.1.0 // indirect
github.com/metacubex/jsonv2 v0.0.0-20260513175203-1c6abea7534c // indirect
github.com/metacubex/nftables v0.0.0-20260426003805-208c2c1ba2cb // indirect
github.com/metacubex/qpack v0.6.0 // indirect
github.com/metacubex/tailscale-wireguard-go v0.0.0-20260513233728-8bc7ee255d04 // indirect
github.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49 // indirect
github.com/mitchellh/go-ps v1.0.0 // indirect
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
github.com/pires/go-proxyproto v0.8.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/safchain/ethtool v0.3.0 // indirect
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect
github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d // indirect
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
github.com/vishvananda/netns v0.0.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/oauth2 v0.24.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/time v0.10.0 // indirect
golang.org/x/tools v0.24.0 // indirect
+48
View File
@@ -5,11 +5,18 @@ github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 h1:cDVUiFo+npB0ZASqnw4
github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk=
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.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4=
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.8.0 h1:MPc2P89IhuVpLI7ETL/2tx3XZ61VeICZjYqDEgNsPRc=
github.com/coreos/go-iptables v0.8.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@@ -33,8 +40,11 @@ github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5d
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtBcj7sBuTTRupn7c2bFspMDIObMJsVK8TeUvpShPok=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I=
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/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
@@ -43,8 +53,12 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=
github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=
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.4.0 h1:EfbpCTjqMuGyq5ZJwxqzn3Cbr2d0rUZU7v5ycAk/e/0=
github.com/gofrs/uuid/v5 v5.4.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -55,11 +69,15 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k=
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I=
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4=
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k=
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 v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I=
github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
@@ -105,6 +123,8 @@ github.com/metacubex/hpke v0.1.0 h1:gu2jUNhraehWi0P/z5HX2md3d7L1FhPQE6/Q0E9r9xQ=
github.com/metacubex/hpke v0.1.0/go.mod h1:vfDm6gfgrwlXUxKDkWbcE44hXtmc1uxLDm2BcR11b3U=
github.com/metacubex/http v0.1.6 h1:xvXuvXMCMxCWMF5nEJF4yiKvXL+p2atWMzs37e80m1I=
github.com/metacubex/http v0.1.6/go.mod h1:Nxx0zZAo2AhRfanyL+fmmK6ACMtVsfpwIl1aFAik2Eg=
github.com/metacubex/jsonv2 v0.0.0-20260513175203-1c6abea7534c h1:KGhBHDe6FveU0ury+9RyX329nclM1CHODa0Fi+uOAYM=
github.com/metacubex/jsonv2 v0.0.0-20260513175203-1c6abea7534c/go.mod h1:F4sVXat6QjPXkNsKRDyyG3BhSkxPFFnRPEIwmmyCgbg=
github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604 h1:hJwCVlE3ojViC35MGHB+FBr8TuIf3BUFn2EQ1VIamsI=
github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604/go.mod h1:lpmN3m269b3V5jFCWtffqBLS4U3QQoIid9ugtO+OhVc=
github.com/metacubex/mhurl v0.1.0 h1:ZdW4Zxe3j3uJ89gNytOazHu6kbHn5owutN/VfXOI8GE=
@@ -145,6 +165,10 @@ github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141 h1:DK2l6m2Fc85H2Bhi
github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141/go.mod h1:/yI4OiGOSn0SURhZdJF3CbtPg3nwK700bG8TZLMBvAg=
github.com/metacubex/ssh v0.1.0 h1:iGfr99qk/eMHzUnQ/0bTxXT8+8SWqLSHBWDHoAhngzw=
github.com/metacubex/ssh v0.1.0/go.mod h1:NUtl0d+/f2cG9ECEpMM8iCVOpmggQlC13oLeDUONDlU=
github.com/metacubex/tailscale v0.0.0-20260514010142-10fd27a2338f h1:nJvp3tZr4Leqh92u8mgj/aT9wUXPrGXhUQ2iXd8KxPI=
github.com/metacubex/tailscale v0.0.0-20260514010142-10fd27a2338f/go.mod h1:2G1V82OGXgxT7m7046GA80I9SlcvczljCK0C7NQ3c10=
github.com/metacubex/tailscale-wireguard-go v0.0.0-20260513233728-8bc7ee255d04 h1:zk+mDDSBl5lv80WWtaFUbpj8XLb7AhjCUbn2pB37N0U=
github.com/metacubex/tailscale-wireguard-go v0.0.0-20260513233728-8bc7ee255d04/go.mod h1:pKUKBy7IcQ5r0i66gWENHgxKvBn8tlgAGx0DZMq8h5M=
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443 h1:H6TnfM12tOoTizYE/qBHH3nEuibIelmHI+BVSxVJr8o=
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
github.com/metacubex/tls v0.1.5 h1:ECcB83dj+zadnhlKcLnUUf1Sq6+vU0f/zoyU0+9oPTc=
@@ -157,6 +181,8 @@ github.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49 h1:lhlqpYHopuTLx9x
github.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49/go.mod h1:MBeEa9IVBphH7vc3LNtW6ZujVXFizotPo3OEiHQ+TNU=
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
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/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU=
github.com/mroth/weightedrand/v2 v2.1.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU=
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2SEPp5+xrS26wEaeb26sZy6k9/ZXlZN+eXE4=
@@ -170,10 +196,14 @@ github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq5
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/pires/go-proxyproto v0.8.0 h1:5unRmEAPbHXHuLjDg01CxJWf91cw3lKHc/0xzKpXEe0=
github.com/pires/go-proxyproto v0.8.0/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
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/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM=
@@ -192,6 +222,14 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d h1:JcGKBZAL7ePLwOhUdN8qGQZlP5GueEiIZwY7R62pejE=
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d/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/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/peercred v0.0.0-20250107143737-35a0c7bd7edc h1:24heQPtnFR+yfntqhI3oAu9i27nEojcQ4NuBQOo5ZFA=
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc/go.mod h1:f93CXfllFsO9ZQVq+Zocb1Gp4G5Fz0b0rXHLOzt/Djc=
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/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
@@ -203,6 +241,8 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
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/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
@@ -213,6 +253,8 @@ gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAo
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 h1:Tl++JLUCe4sxGu8cTpDzRLd3tN7US4hOxG5YpKCzkek=
go4.org/mem v0.0.0-20240501181205-ae6ca9944745/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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -229,7 +271,10 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -238,12 +283,14 @@ golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
@@ -258,3 +305,4 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
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=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
software.sslmate.com/src/go-pkcs12 v0.2.1 h1:tbT1jjaeFOF230tzOIRJ6U5S1jNqpsSyNjzDd58H3J8=
+9
View File
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"io"
"net"
"net/netip"
"path/filepath"
@@ -65,6 +66,7 @@ var (
sniffingEnable = false
ruleUpdateCallback = utils.NewCallback[P.RuleProvider]()
runningCallback = utils.NewCallback[struct{}]()
)
type tunnel struct{}
@@ -134,6 +136,13 @@ func OnInnerLoading() {
func OnRunning() {
status.Store(Running)
runningCallback.Emit(struct{}{})
}
func RegisterOnRunning(callback func()) io.Closer {
return runningCallback.Register(func(struct{}) {
callback()
})
}
func Status() TunnelStatus {