Skip to content

Commit 96a4dc1

Browse files
committed
new dnstt transport
1 parent 30fa26b commit 96a4dc1

7 files changed

Lines changed: 231 additions & 1 deletion

File tree

constant/v2ray.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ const (
77
V2RayTransportTypeGRPC = "grpc"
88
V2RayTransportTypeHTTPUpgrade = "httpupgrade"
99
V2RayTransportTypeXHTTP = "xhttp"
10+
V2RayTransportTypeDNSTT = "dnstt"
1011
)

go.mod

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,10 @@ require (
144144
github.com/kamstrup/intmap v0.5.2 // indirect
145145
github.com/klauspost/compress v1.18.0 // indirect
146146
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
147+
github.com/klauspost/reedsolomon v1.12.0 // indirect
147148
github.com/libdns/libdns v1.1.1 // indirect
148149
github.com/libp2p/go-reuseport v0.4.0 // indirect
150+
github.com/mahsanet/dnstt v0.0.0-20260208072932-d75b50056612 // indirect
149151
github.com/marusama/semaphore v0.0.0-20171214154724-565ffd8e868a // indirect
150152
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 // indirect
151153
github.com/mdlayher/socket v0.5.1 // indirect
@@ -215,11 +217,16 @@ require (
215217
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect
216218
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect
217219
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 // indirect
220+
github.com/templexxx/cpu v0.1.0 // indirect
221+
github.com/templexxx/xorsimd v0.4.2 // indirect
218222
github.com/tidwall/sjson v1.2.5 // indirect
223+
github.com/tjfoc/gmsm v1.4.1 // indirect
219224
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
220225
github.com/wader/filtertransport v0.0.0-20200316221534-bdd9e61eee78 // indirect
221226
github.com/wlynxg/anet v0.0.5 // indirect
222227
github.com/x448/float16 v0.8.4 // indirect
228+
github.com/xtaci/kcp-go/v5 v5.6.8 // indirect
229+
github.com/xtaci/smux v1.5.24 // indirect
223230
github.com/zeebo/blake3 v0.2.4 // indirect
224231
gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/goptlib v1.5.0 // indirect
225232
go.uber.org/multierr v1.11.0 // indirect

go.sum

Lines changed: 75 additions & 0 deletions
Large diffs are not rendered by default.

option/dnstt.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package option
2+
3+
type DnsttOptions struct {
4+
PublicKey string `json:"publicKey"`
5+
Domain string `json:"domain"`
6+
Resolvers []string `json:"resolvers"`
7+
}

option/v2ray_transport.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type _V2RayTransportOptions struct {
2121
GRPCOptions V2RayGRPCOptions `json:"-"`
2222
HTTPUpgradeOptions V2RayHTTPUpgradeOptions `json:"-"`
2323
XHTTPOptions V2RayXHTTPOptions `json:"-"`
24+
DNSTTOptions DnsttOptions `json:"-"`
2425
}
2526

2627
type V2RayTransportOptions _V2RayTransportOptions
@@ -40,6 +41,8 @@ func (o V2RayTransportOptions) MarshalJSON() ([]byte, error) {
4041
v = o.HTTPUpgradeOptions
4142
case C.V2RayTransportTypeXHTTP:
4243
v = o.XHTTPOptions
44+
case C.V2RayTransportTypeDNSTT:
45+
v = o.DNSTTOptions
4346
case "":
4447
return nil, E.New("missing transport type")
4548
default:

protocol/vmess/outbound.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
5757
if err != nil {
5858
return nil, err
5959
}
60-
outbound.tlsDialer = tls.NewDialer(outboundDialer, outbound.tlsConfig)
60+
if outbound.tlsConfig != nil {
61+
outbound.tlsDialer = tls.NewDialer(outboundDialer, outbound.tlsConfig)
62+
}
6163
}
6264
if options.Transport != nil {
6365
outbound.transport, err = v2ray.NewClientTransport(ctx, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig)

transport/v2raydnstt/dnstt.go

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package v2raydnstt
2+
3+
import (
4+
"context"
5+
"sync"
6+
7+
"github.com/sagernet/sing-box/common/tls"
8+
9+
"fmt"
10+
"log"
11+
"net"
12+
13+
dnstt "github.com/mahsanet/dnstt/client"
14+
"github.com/sagernet/sing-box/adapter"
15+
"github.com/sagernet/sing-box/option"
16+
E "github.com/sagernet/sing/common/exceptions"
17+
M "github.com/sagernet/sing/common/metadata"
18+
N "github.com/sagernet/sing/common/network"
19+
)
20+
21+
var _ adapter.V2RayClientTransport = (*Client)(nil)
22+
23+
type Client struct {
24+
ctx context.Context
25+
dialer N.Dialer
26+
resolvers []dnstt.Resolver
27+
publicKey string
28+
domain string
29+
tunnel *dnstt.Tunnel
30+
mu sync.Mutex
31+
}
32+
33+
// Close implements [adapter.V2RayClientTransport].
34+
func (c *Client) Close() error {
35+
36+
}
37+
38+
func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.DnsttOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) {
39+
resolvers := []dnstt.Resolver{}
40+
for _, resolverAddr := range options.Resolvers {
41+
resolver, err := dnstt.NewResolver(dnstt.ResolverTypeUDP, resolverAddr)
42+
if err != nil {
43+
return nil, fmt.Errorf("invalid resolver address %s: %w", resolverAddr, err)
44+
}
45+
resolvers = append(resolvers, resolver)
46+
}
47+
48+
if len(resolvers) == 0 {
49+
return nil, E.New("at least one resolver is required")
50+
}
51+
52+
if options.PublicKey == "" {
53+
return nil, E.New("public key is required")
54+
}
55+
56+
if options.Domain == "" {
57+
return nil, E.New("domain is required")
58+
}
59+
return &Client{
60+
ctx: ctx,
61+
dialer: dialer,
62+
domain: options.Domain,
63+
publicKey: options.PublicKey,
64+
resolvers: resolvers,
65+
}, nil
66+
}
67+
68+
func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
69+
70+
tunnel, err := c.establishDnsttTunnel(ctx)
71+
if err != nil {
72+
return nil, fmt.Errorf("failed to get or establish tunnel: %w", err)
73+
}
74+
75+
stream, err := tunnel.OpenStream()
76+
if err != nil {
77+
return nil, fmt.Errorf("failed to open stream: %w", err)
78+
}
79+
80+
return stream, nil
81+
}
82+
83+
func (c *Client) establishDnsttTunnel(ctx context.Context) (*dnstt.Tunnel, error) {
84+
// dnsttConfig := streamSettings.ProtocolSettings.(*Config)
85+
c.mu.Lock()
86+
defer c.mu.Unlock()
87+
if c.tunnel != nil {
88+
return c.tunnel, nil
89+
}
90+
tServer, err := dnstt.NewTunnelServer(c.domain, c.publicKey)
91+
if err != nil {
92+
return nil, fmt.Errorf("invalid tunnel server: %w", err)
93+
}
94+
95+
tunnelServers := []dnstt.TunnelServer{tServer}
96+
97+
resolver := c.resolvers[0]
98+
tunnelServer := tunnelServers[0]
99+
100+
tunnel, err := dnstt.NewTunnel(resolver, tunnelServer)
101+
if err != nil {
102+
return nil, fmt.Errorf("failed to create tunnel: %w", err)
103+
}
104+
105+
if err := tunnel.InitiateResolverConnection(); err != nil {
106+
return nil, fmt.Errorf("failed to initiate connection to resolver: %w", err)
107+
}
108+
109+
if err := tunnel.InitiateDNSPacketConn(tunnelServer.Addr); err != nil {
110+
return nil, fmt.Errorf("failed to initiate DNS packet connection: %w", err)
111+
}
112+
113+
log.Printf("effective MTU %d", tunnelServer.MTU)
114+
115+
if err := tunnel.InitiateKCPConn(tunnelServer.MTU); err != nil {
116+
return nil, fmt.Errorf("failed to initiate KCP connection: %w", err)
117+
}
118+
119+
log.Printf("established KCP conn")
120+
121+
if err := tunnel.InitiateNoiseChannel(); err != nil {
122+
log.Printf("failed to establish Noise channel: %v", err)
123+
return nil, fmt.Errorf("failed to initiate Noise channel: %w", err)
124+
}
125+
126+
log.Printf("established Noise channel")
127+
128+
if err := tunnel.InitiateSmuxSession(); err != nil {
129+
return nil, fmt.Errorf("failed to initiate smux session: %w", err)
130+
}
131+
132+
c.tunnel = tunnel
133+
log.Printf("established smux session")
134+
return tunnel, nil
135+
}

0 commit comments

Comments
 (0)