Refactor UDP DNS hijack
This commit is contained in:
@@ -19,6 +19,7 @@ type Router interface {
|
||||
Lifecycle
|
||||
ConnectionRouter
|
||||
PreMatch(metadata InboundContext, firstPacket []byte) PreMatchResult
|
||||
HijackDNSPacket(ctx context.Context, payload []byte, writer N.PacketWriter, metadata InboundContext)
|
||||
ConnectionRouterEx
|
||||
RuleSet(tag string) (RuleSet, bool)
|
||||
Rules() []Rule
|
||||
@@ -37,6 +38,7 @@ const (
|
||||
PreMatchReject
|
||||
PreMatchDrop
|
||||
PreMatchBypass
|
||||
PreMatchHijackDNS
|
||||
)
|
||||
|
||||
type PreMatchResult struct {
|
||||
@@ -92,6 +94,8 @@ func JudgeFlow(router Router, inbound string, inboundType string, network uint8,
|
||||
return tun.FlowVerdict{Action: tun.ActionDrop}
|
||||
case PreMatchBypass:
|
||||
return tun.FlowVerdict{Action: tun.ActionBypass}
|
||||
case PreMatchHijackDNS:
|
||||
return tun.FlowVerdict{Action: tun.ActionHijackDNS}
|
||||
default:
|
||||
return tun.FlowVerdict{Action: tun.ActionAccept}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ require (
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.1
|
||||
github.com/sagernet/sing-shadowtls v0.2.1
|
||||
github.com/sagernet/sing-snell v0.0.0-20260710094516-a4e97ee24beb
|
||||
github.com/sagernet/sing-tun v0.8.12-0.20260717024008-39eed1f6361d
|
||||
github.com/sagernet/sing-tun v0.8.12-0.20260719050255-7e7bd198288c
|
||||
github.com/sagernet/sing-usbip v0.0.0-20260616101517-efb91521eddb
|
||||
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1
|
||||
github.com/sagernet/smux v1.5.50-sing-box-mod.1
|
||||
|
||||
@@ -299,8 +299,8 @@ github.com/sagernet/sing-shadowtls v0.2.1 h1:ZiHZdnEnP+YS73NMsxiZmIFCwNd0M4k7PkG
|
||||
github.com/sagernet/sing-shadowtls v0.2.1/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
|
||||
github.com/sagernet/sing-snell v0.0.0-20260710094516-a4e97ee24beb h1:VvU2/PZqP5tbKTDq0BxkhRO8ZnKI4UJzziakgBiP2Qg=
|
||||
github.com/sagernet/sing-snell v0.0.0-20260710094516-a4e97ee24beb/go.mod h1:PcwzX/Xvqky0EP3kGt8OCjYb3R1pydenPHNQZcPZmXY=
|
||||
github.com/sagernet/sing-tun v0.8.12-0.20260717024008-39eed1f6361d h1:UXUGxGTwotY+R+QkRGvhj/N148SDnwMh/d9Hefn5X7Y=
|
||||
github.com/sagernet/sing-tun v0.8.12-0.20260717024008-39eed1f6361d/go.mod h1:F/gRq5VX1WN/OZtsvbN2JjXXuNl2ATJglHMSk1/iN9U=
|
||||
github.com/sagernet/sing-tun v0.8.12-0.20260719050255-7e7bd198288c h1:jS5eWD9PyXhHY89DueRDgenfcrfpyPOuuZIoNfnBmOs=
|
||||
github.com/sagernet/sing-tun v0.8.12-0.20260719050255-7e7bd198288c/go.mod h1:F/gRq5VX1WN/OZtsvbN2JjXXuNl2ATJglHMSk1/iN9U=
|
||||
github.com/sagernet/sing-usbip v0.0.0-20260616101517-efb91521eddb h1:KEMbfexD4DvrQGYWwx6r+AwH9Veh8z6cnBZmtCS2G+0=
|
||||
github.com/sagernet/sing-usbip v0.0.0-20260616101517-efb91521eddb/go.mod h1:D4CnJX3MNAAANhbQUxfIRgBdnvlTEaV7h6ojedcs+pw=
|
||||
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 h1:aSwUNYUkVyVvdmBSufR8/nRFonwJeKSIROxHcm5br9o=
|
||||
|
||||
@@ -420,6 +420,10 @@ func (e *Endpoint) JudgeFlow(network uint8, source netip.AddrPort, destination n
|
||||
return judgeOpenConnectFlow(e.router, e.Tag(), e.Type(), e.state.Load().localAddresses, network, source, destination, firstPacket)
|
||||
}
|
||||
|
||||
func (e *Endpoint) NewDNSPacket(payload []byte, source M.Socksaddr, destination M.Socksaddr, writer N.PacketWriter) {
|
||||
e.newDNSPacket(log.ContextWithNewID(e.loopContext), e, payload, source, destination, writer)
|
||||
}
|
||||
|
||||
func (e *Endpoint) ready() bool {
|
||||
state := e.state.Load()
|
||||
return state.started && state.tunnelConfigured
|
||||
|
||||
@@ -68,6 +68,18 @@ func (e *endpointBase) newPacketConnection(ctx context.Context, endpoint adapter
|
||||
e.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (e *endpointBase) newDNSPacket(ctx context.Context, endpoint adapter.Endpoint, payload []byte, source M.Socksaddr, destination M.Socksaddr, writer N.PacketWriter) {
|
||||
var metadata adapter.InboundContext
|
||||
metadata.Inbound = endpoint.Tag()
|
||||
metadata.InboundType = endpoint.Type()
|
||||
metadata.Network = N.NetworkUDP
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
metadata.Protocol = C.ProtocolDNS
|
||||
e.logger.InfoContext(ctx, "inbound DNS packet from ", source)
|
||||
e.router.HijackDNSPacket(ctx, payload, writer, metadata)
|
||||
}
|
||||
|
||||
func isEndpointLocalAddress(localAddresses []netip.Prefix, address netip.Addr) bool {
|
||||
for _, localPrefix := range localAddresses {
|
||||
if address == localPrefix.Addr() {
|
||||
|
||||
@@ -477,6 +477,10 @@ func (c *ClientEndpoint) JudgeFlow(network uint8, source netip.AddrPort, destina
|
||||
return judgeOpenVPNFlow(c.router, c.Tag(), c.Type(), c.state.Load().localAddresses, network, source, destination, firstPacket)
|
||||
}
|
||||
|
||||
func (c *ClientEndpoint) NewDNSPacket(payload []byte, source M.Socksaddr, destination M.Socksaddr, writer N.PacketWriter) {
|
||||
c.newDNSPacket(log.ContextWithNewID(c.ctx), c, payload, source, destination, writer)
|
||||
}
|
||||
|
||||
func (c *ClientEndpoint) ready() bool {
|
||||
state := c.state.Load()
|
||||
return state.started && state.tunnelConfigured
|
||||
|
||||
@@ -72,6 +72,18 @@ func (e *endpointBase) newPacketConnection(ctx context.Context, endpoint adapter
|
||||
e.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (e *endpointBase) newDNSPacket(ctx context.Context, endpoint adapter.Endpoint, payload []byte, source M.Socksaddr, destination M.Socksaddr, writer N.PacketWriter) {
|
||||
var metadata adapter.InboundContext
|
||||
metadata.Inbound = endpoint.Tag()
|
||||
metadata.InboundType = endpoint.Type()
|
||||
metadata.Network = N.NetworkUDP
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
metadata.Protocol = C.ProtocolDNS
|
||||
e.logger.InfoContext(ctx, "inbound DNS packet from ", source)
|
||||
e.router.HijackDNSPacket(ctx, payload, writer, metadata)
|
||||
}
|
||||
|
||||
func isEndpointLocalAddress(localAddresses []netip.Prefix, address netip.Addr) bool {
|
||||
for _, localPrefix := range localAddresses {
|
||||
if address == localPrefix.Addr() {
|
||||
|
||||
@@ -484,6 +484,10 @@ func (s *ServerEndpoint) JudgeFlow(network uint8, source netip.AddrPort, destina
|
||||
return judgeOpenVPNFlow(s.router, s.Tag(), s.Type(), s.localAddresses, network, source, destination, firstPacket)
|
||||
}
|
||||
|
||||
func (s *ServerEndpoint) NewDNSPacket(payload []byte, source M.Socksaddr, destination M.Socksaddr, writer N.PacketWriter) {
|
||||
s.newDNSPacket(log.ContextWithNewID(s.ctx), s, payload, source, destination, writer)
|
||||
}
|
||||
|
||||
func (s *ServerEndpoint) WritePackets(packets [][]byte) error {
|
||||
if !s.started.Load() {
|
||||
return E.New("OpenVPN server is not ready yet")
|
||||
|
||||
@@ -6,9 +6,13 @@ import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing-tun/gtcpip/header"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
tsTUN "github.com/sagernet/tailscale/net/tstun"
|
||||
"github.com/sagernet/tailscale/types/ipproto"
|
||||
"github.com/sagernet/tailscale/wgengine/filter"
|
||||
@@ -67,6 +71,19 @@ func (t *Endpoint) JudgeFlow(network uint8, source netip.AddrPort, destination n
|
||||
return adapter.JudgeFlow(t.router, t.Tag(), t.Type(), network, source, destination, firstPacket)
|
||||
}
|
||||
|
||||
func (t *Endpoint) NewDNSPacket(payload []byte, source M.Socksaddr, destination M.Socksaddr, writer N.PacketWriter) {
|
||||
ctx := log.ContextWithNewID(t.ctx)
|
||||
var metadata adapter.InboundContext
|
||||
metadata.Inbound = t.Tag()
|
||||
metadata.InboundType = t.Type()
|
||||
metadata.Network = N.NetworkUDP
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
metadata.Protocol = C.ProtocolDNS
|
||||
t.logger.InfoContext(ctx, "inbound DNS packet from ", source)
|
||||
t.router.HijackDNSPacket(ctx, payload, writer, metadata)
|
||||
}
|
||||
|
||||
func (t *Endpoint) AttachReturn(returnPath tun.Return) error {
|
||||
t.returnAccess.Lock()
|
||||
defer t.returnAccess.Unlock()
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing-tun/gtcpip/header"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
@@ -516,11 +517,27 @@ func (t *Inbound) Close() error {
|
||||
|
||||
func (t *Inbound) JudgeFlow(network uint8, source netip.AddrPort, destination netip.AddrPort, firstPacket []byte) tun.FlowVerdict {
|
||||
if slices.Contains(t.dnsHijackAddress, destination.Addr()) {
|
||||
if network == uint8(header.UDPProtocolNumber) {
|
||||
return tun.FlowVerdict{Action: tun.ActionHijackDNS}
|
||||
}
|
||||
return tun.FlowVerdict{Action: tun.ActionAccept}
|
||||
}
|
||||
return adapter.JudgeFlow(t.router, t.tag, C.TypeTun, network, source, destination, firstPacket)
|
||||
}
|
||||
|
||||
func (t *Inbound) NewDNSPacket(payload []byte, source M.Socksaddr, destination M.Socksaddr, writer N.PacketWriter) {
|
||||
ctx := log.ContextWithNewID(t.ctx)
|
||||
var metadata adapter.InboundContext
|
||||
metadata.Inbound = t.tag
|
||||
metadata.InboundType = C.TypeTun
|
||||
metadata.Network = N.NetworkUDP
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
metadata.Protocol = C.ProtocolDNS
|
||||
t.logger.InfoContext(ctx, "inbound DNS packet from ", source)
|
||||
t.router.HijackDNSPacket(ctx, payload, writer, metadata)
|
||||
}
|
||||
|
||||
func (t *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
var metadata adapter.InboundContext
|
||||
@@ -591,3 +608,7 @@ func (t *autoRedirectHandler) NewConnectionEx(ctx context.Context, conn net.Conn
|
||||
func (t *autoRedirectHandler) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
panic("unexcepted")
|
||||
}
|
||||
|
||||
func (t *autoRedirectHandler) NewDNSPacket(payload []byte, source M.Socksaddr, destination M.Socksaddr, writer N.PacketWriter) {
|
||||
(*Inbound)(t).NewDNSPacket(payload, source, destination, writer)
|
||||
}
|
||||
|
||||
@@ -188,6 +188,19 @@ func (w *Endpoint) JudgeFlow(network uint8, source netip.AddrPort, destination n
|
||||
return adapter.JudgeFlow(w.router, w.Tag(), w.Type(), network, source, destination, firstPacket)
|
||||
}
|
||||
|
||||
func (w *Endpoint) NewDNSPacket(payload []byte, source M.Socksaddr, destination M.Socksaddr, writer N.PacketWriter) {
|
||||
ctx := log.ContextWithNewID(w.ctx)
|
||||
var metadata adapter.InboundContext
|
||||
metadata.Inbound = w.Tag()
|
||||
metadata.InboundType = w.Type()
|
||||
metadata.Network = N.NetworkUDP
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
metadata.Protocol = C.ProtocolDNS
|
||||
w.logger.InfoContext(ctx, "inbound DNS packet from ", source)
|
||||
w.router.HijackDNSPacket(ctx, payload, writer, metadata)
|
||||
}
|
||||
|
||||
func (w *Endpoint) WritePackets(packets [][]byte) error {
|
||||
if !w.started.Load() {
|
||||
return E.New("WireGuard is not ready yet")
|
||||
|
||||
+18
-56
@@ -10,12 +10,9 @@ import (
|
||||
"github.com/sagernet/sing-box/dns"
|
||||
dnsOutbound "github.com/sagernet/sing-box/protocol/dns"
|
||||
R "github.com/sagernet/sing-box/route/rule"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
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/udpnat2"
|
||||
|
||||
mDNS "github.com/miekg/dns"
|
||||
)
|
||||
@@ -36,24 +33,6 @@ func (r *Router) hijackDNSStream(ctx context.Context, conn net.Conn, metadata ad
|
||||
}
|
||||
|
||||
func (r *Router) hijackDNSPacket(ctx context.Context, conn N.PacketConn, packetBuffers []*N.PacketBuffer, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error {
|
||||
if natConn, isNatConn := conn.(udpnat.Conn); isNatConn {
|
||||
metadata.Destination = M.Socksaddr{}
|
||||
for _, packet := range packetBuffers {
|
||||
buffer := packet.Buffer
|
||||
destination := packet.Destination
|
||||
N.PutPacketBuffer(packet)
|
||||
go ExchangeDNSPacket(ctx, r.dns, r.logger, natConn, buffer, metadata, destination)
|
||||
}
|
||||
natConn.SetHandler(&dnsHijacker{
|
||||
router: r.dns,
|
||||
logger: r.logger,
|
||||
conn: conn,
|
||||
ctx: ctx,
|
||||
metadata: metadata,
|
||||
onClose: onClose,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
err := dnsOutbound.NewDNSPacketConnection(ctx, r.dns, conn, packetBuffers, metadata)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil && !E.IsClosedOrCanceled(err) {
|
||||
@@ -62,48 +41,31 @@ func (r *Router) hijackDNSPacket(ctx context.Context, conn N.PacketConn, packetB
|
||||
return nil
|
||||
}
|
||||
|
||||
func ExchangeDNSPacket(ctx context.Context, router adapter.DNSRouter, logger logger.ContextLogger, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext, destination M.Socksaddr) {
|
||||
err := exchangeDNSPacket(ctx, router, conn, buffer, metadata, destination)
|
||||
if err != nil && !R.IsRejected(err) && !E.IsClosedOrCanceled(err) {
|
||||
logger.ErrorContext(ctx, E.Cause(err, "process DNS packet"))
|
||||
}
|
||||
}
|
||||
|
||||
func exchangeDNSPacket(ctx context.Context, router adapter.DNSRouter, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext, destination M.Socksaddr) error {
|
||||
func (r *Router) HijackDNSPacket(ctx context.Context, payload []byte, writer N.PacketWriter, metadata adapter.InboundContext) {
|
||||
var message mDNS.Msg
|
||||
err := message.Unpack(buffer.Bytes())
|
||||
buffer.Release()
|
||||
err := message.Unpack(payload)
|
||||
if err != nil {
|
||||
return E.Cause(err, "unpack request")
|
||||
r.logger.ErrorContext(ctx, E.Cause(err, "process DNS packet: unpack request"))
|
||||
return
|
||||
}
|
||||
response, err := router.Exchange(adapter.WithContext(ctx, &metadata), &message, adapter.DNSQueryOptions{})
|
||||
destination := metadata.Destination
|
||||
metadata.Destination = M.Socksaddr{}
|
||||
go func() {
|
||||
exchangeErr := r.exchangeDNSPacket(ctx, &message, writer, metadata, destination)
|
||||
if exchangeErr != nil && !R.IsRejected(exchangeErr) && !E.IsClosedOrCanceled(exchangeErr) {
|
||||
r.logger.ErrorContext(ctx, E.Cause(exchangeErr, "process DNS packet"))
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (r *Router) exchangeDNSPacket(ctx context.Context, message *mDNS.Msg, writer N.PacketWriter, metadata adapter.InboundContext, destination M.Socksaddr) error {
|
||||
response, err := r.dns.Exchange(adapter.WithContext(ctx, &metadata), message, adapter.DNSQueryOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
responseBuffer, err := dns.TruncateDNSMessage(&message, response, 1024)
|
||||
responseBuffer, err := dns.TruncateDNSMessage(message, response, 1024)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = conn.WritePacket(responseBuffer, destination)
|
||||
return err
|
||||
}
|
||||
|
||||
type dnsHijacker struct {
|
||||
router adapter.DNSRouter
|
||||
logger logger.ContextLogger
|
||||
conn N.PacketConn
|
||||
ctx context.Context
|
||||
metadata adapter.InboundContext
|
||||
onClose N.CloseHandlerFunc
|
||||
}
|
||||
|
||||
func (h *dnsHijacker) NewPacketEx(buffer *buf.Buffer, destination M.Socksaddr) {
|
||||
go ExchangeDNSPacket(h.ctx, h.router, h.logger, h.conn, buffer, h.metadata, destination)
|
||||
}
|
||||
|
||||
func (h *dnsHijacker) Close() error {
|
||||
if h.onClose != nil {
|
||||
h.onClose(nil)
|
||||
}
|
||||
return nil
|
||||
return writer.WritePacket(responseBuffer, destination)
|
||||
}
|
||||
|
||||
@@ -391,6 +391,11 @@ func (r *Router) PreMatch(metadata adapter.InboundContext, firstPacket []byte) a
|
||||
return adapter.PreMatchResult{Action: adapter.PreMatchDrop}
|
||||
}
|
||||
return adapter.PreMatchResult{Action: adapter.PreMatchReject}
|
||||
case *R.RuleActionHijackDNS:
|
||||
if metadata.Network != N.NetworkUDP {
|
||||
return continueResult
|
||||
}
|
||||
return adapter.PreMatchResult{Action: adapter.PreMatchHijackDNS}
|
||||
case *R.RuleActionResolve:
|
||||
resolveErr := r.actionResolve(adapter.WithContext(ctx, &metadata), &metadata, action)
|
||||
if resolveErr != nil {
|
||||
|
||||
@@ -24,6 +24,9 @@ func (r *ruleSetItemTestRouter) PreMatch(adapter.InboundContext, []byte) adapter
|
||||
return adapter.PreMatchResult{}
|
||||
}
|
||||
|
||||
func (r *ruleSetItemTestRouter) HijackDNSPacket(context.Context, []byte, N.PacketWriter, adapter.InboundContext) {
|
||||
}
|
||||
|
||||
func (r *ruleSetItemTestRouter) RouteConnection(context.Context, net.Conn, adapter.InboundContext) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -48,6 +48,9 @@ func (h *endpointUDPNATHandler) JudgeFlow(uint8, netip.AddrPort, netip.AddrPort,
|
||||
return tun.FlowVerdict{Action: tun.ActionAccept}
|
||||
}
|
||||
|
||||
func (h *endpointUDPNATHandler) NewDNSPacket([]byte, M.Socksaddr, M.Socksaddr, N.PacketWriter) {
|
||||
}
|
||||
|
||||
func (h *endpointUDPNATHandler) NewConnectionEx(_ context.Context, conn net.Conn, _ M.Socksaddr, _ M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
err := conn.Close()
|
||||
if onClose != nil {
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ require (
|
||||
github.com/sagernet/sing-quic v0.6.4-0.20260709034545-e23afe1172dc
|
||||
github.com/sagernet/sing-shadowsocks v0.2.8
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.1
|
||||
github.com/sagernet/sing-tun v0.8.12-0.20260717024008-39eed1f6361d
|
||||
github.com/sagernet/sing-tun v0.8.12-0.20260719050255-7e7bd198288c
|
||||
github.com/spyzhov/ajson v0.9.4
|
||||
github.com/stretchr/testify v1.11.1
|
||||
go.uber.org/goleak v1.3.0
|
||||
|
||||
+2
-2
@@ -318,8 +318,8 @@ github.com/sagernet/sing-shadowtls v0.2.1 h1:ZiHZdnEnP+YS73NMsxiZmIFCwNd0M4k7PkG
|
||||
github.com/sagernet/sing-shadowtls v0.2.1/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
|
||||
github.com/sagernet/sing-snell v0.0.0-20260710094516-a4e97ee24beb h1:VvU2/PZqP5tbKTDq0BxkhRO8ZnKI4UJzziakgBiP2Qg=
|
||||
github.com/sagernet/sing-snell v0.0.0-20260710094516-a4e97ee24beb/go.mod h1:PcwzX/Xvqky0EP3kGt8OCjYb3R1pydenPHNQZcPZmXY=
|
||||
github.com/sagernet/sing-tun v0.8.12-0.20260717024008-39eed1f6361d h1:UXUGxGTwotY+R+QkRGvhj/N148SDnwMh/d9Hefn5X7Y=
|
||||
github.com/sagernet/sing-tun v0.8.12-0.20260717024008-39eed1f6361d/go.mod h1:F/gRq5VX1WN/OZtsvbN2JjXXuNl2ATJglHMSk1/iN9U=
|
||||
github.com/sagernet/sing-tun v0.8.12-0.20260719050255-7e7bd198288c h1:jS5eWD9PyXhHY89DueRDgenfcrfpyPOuuZIoNfnBmOs=
|
||||
github.com/sagernet/sing-tun v0.8.12-0.20260719050255-7e7bd198288c/go.mod h1:F/gRq5VX1WN/OZtsvbN2JjXXuNl2ATJglHMSk1/iN9U=
|
||||
github.com/sagernet/sing-usbip v0.0.0-20260616101517-efb91521eddb h1:KEMbfexD4DvrQGYWwx6r+AwH9Veh8z6cnBZmtCS2G+0=
|
||||
github.com/sagernet/sing-usbip v0.0.0-20260616101517-efb91521eddb/go.mod h1:D4CnJX3MNAAANhbQUxfIRgBdnvlTEaV7h6ojedcs+pw=
|
||||
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 h1:aSwUNYUkVyVvdmBSufR8/nRFonwJeKSIROxHcm5br9o=
|
||||
|
||||
Reference in New Issue
Block a user