Documentation
¶
Overview ¶
Package client provides a reusable DNS tunnel client library.
It provides configuration options for VayDNS features (DoH/DoT transports, per-query UDP, forged response filtering, rate limiting, dnstt wire compatibility, etc.).
Basic usage (xray-core compatible):
r, _ := client.NewResolver(client.ResolverTypeUDP, "8.8.8.8:53")
ts, _ := client.NewTunnelServer("t.example.com", "pubkey-hex")
t, _ := client.NewTunnel(r, ts)
t.InitiateResolverConnection()
t.InitiateDNSPacketConn(ts.Addr)
t.InitiateKCPConn(ts.MTU)
t.InitiateNoiseChannel()
t.InitiateSmuxSession()
stream, _ := t.OpenStream() // returns net.Conn
defer t.Close()
Index ¶
- Constants
- func DNSNameCapacity(domain dns.Name, maxQnameLen int, maxNumLabels int) int
- func NewUDPPacketConn(remoteAddr net.Addr, ...) (*UDPPacketConn, *ForgedStats, error)
- func NewUTLSRoundTripper(config *utls.Config, id *utls.ClientHelloID) *utlsRoundTripper
- func SampleUTLSDistribution(spec string) (*utls.ClientHelloID, error)
- func UTLSClientHelloIDMap() []struct{ ... }
- func UTLSDialContext(ctx context.Context, network, addr string, config *utls.Config, ...) (*utls.UConn, error)
- func UTLSLookup(label string) *utls.ClientHelloID
- type DNSPacketConn
- type ForgedStats
- type HTTPPacketConn
- type Outbound
- type RateLimiter
- type Resolver
- type ResolverType
- type TLSPacketConn
- type Tunnel
- func (t *Tunnel) Close() error
- func (t *Tunnel) Handle(lconn *net.TCPConn) error
- func (t *Tunnel) InitiateDNSPacketConn(domain dns.Name) error
- func (t *Tunnel) InitiateKCPConn(mtu int) error
- func (t *Tunnel) InitiateNoiseChannel() error
- func (t *Tunnel) InitiateResolverConnection() error
- func (t *Tunnel) InitiateSmuxSession() error
- func (t *Tunnel) ListenAndServe(listenAddr string) error
- func (t *Tunnel) OpenStream() (net.Conn, error)
- type TunnelServer
- type UDPPacketConn
Constants ¶
const ( DefaultIdleTimeout = 60 * time.Second DefaultKeepAlive = 10 * time.Second DefaultOpenStreamTimeout = 10 * time.Second DefaultReconnectDelay = 1 * time.Second DefaultReconnectMaxDelay = 30 * time.Second DefaultSessionCheckInterval = 20 * time.Second DefaultUDPResponseTimeout = 500 * time.Millisecond DefaultUDPWorkers = 100 DefaultMaxStreams = 256 DefaultHandshakeTimeout = 15 * time.Second )
Default timeouts for VayDNS mode.
const ( DnsttIdleTimeout = 2 * time.Minute DnsttKeepAlive = 10 * time.Second )
Default timeouts for dnstt compatibility mode.
Variables ¶
This section is empty.
Functions ¶
func DNSNameCapacity ¶
DNSNameCapacity returns the number of raw bytes that can be encoded in a DNS query name, given the domain suffix and encoding constraints.
func NewUDPPacketConn ¶
func NewUDPPacketConn(remoteAddr net.Addr, dialerControl func(network, address string, c syscall.RawConn) error, numWorkers int, responseTimeout time.Duration, ignoreErrors bool, queueSize int, overflowMode turbotunnel.QueueOverflowMode) (*UDPPacketConn, *ForgedStats, error)
NewUDPPacketConn creates a UDPPacketConn with numWorkers goroutines that each send one query at a time on a fresh UDP socket. The returned ForgedStats pointer is shared with the caller so DNSPacketConn can include per-query forged counts in its reporting.
func NewUTLSRoundTripper ¶
func NewUTLSRoundTripper(config *utls.Config, id *utls.ClientHelloID) *utlsRoundTripper
NewUTLSRoundTripper creates a utlsRoundTripper with the given TLS configuration and ClientHelloID.
func SampleUTLSDistribution ¶
func SampleUTLSDistribution(spec string) (*utls.ClientHelloID, error)
SampleUTLSDistribution parses a weighted distribution string (e.g., "3*Firefox,2*Chrome,1*iOS") and randomly selects a ClientHelloID.
func UTLSClientHelloIDMap ¶
func UTLSClientHelloIDMap() []struct {
Label string
ID *utls.ClientHelloID
}
UTLSClientHelloIDMap returns the list of supported uTLS fingerprint labels.
func UTLSDialContext ¶
func UTLSDialContext(ctx context.Context, network, addr string, config *utls.Config, id *utls.ClientHelloID) (*utls.UConn, error)
utlsDialContext connects to the given network address and initiates a TLS handshake with the provided ClientHelloID, and returns the resulting TLS connection. UTLSDialContext connects to the given network address and initiates a TLS handshake with the provided ClientHelloID, and returns the resulting TLS connection.
func UTLSLookup ¶
func UTLSLookup(label string) *utls.ClientHelloID
UTLSLookup returns a *utls.ClientHelloID by case-insensitive label match, or nil if there is no match.
Types ¶
type DNSPacketConn ¶
type DNSPacketConn struct {
// QueuePacketConn is the direct receiver of ReadFrom and WriteTo calls.
// recvLoop and sendLoop take the messages out of the receive and send
// queues and actually put them on the network.
*turbotunnel.QueuePacketConn
// contains filtered or unexported fields
}
DNSPacketConn provides a packet-sending and -receiving interface over various forms of DNS. It handles the details of how packets and padding are encoded as a DNS name in the Question section of an upstream query, and as a TXT RR in downstream responses.
DNSPacketConn does not handle the mechanics of actually sending and receiving encoded DNS messages. That is rather the responsibility of some other net.PacketConn such as net.UDPConn, HTTPPacketConn, or TLSPacketConn, one of which must be provided to NewDNSPacketConn.
We don't have a need to match up a query and a response by ID. Queries and responses are vehicles for carrying data and for our purposes don't need to be correlated. When sending a query, we generate a random ID, and when receiving a response, we ignore the ID.
func NewDNSPacketConn ¶
func NewDNSPacketConn(transport net.PacketConn, addr net.Addr, domain dns.Name, rateLimiter *RateLimiter, maxQnameLen int, maxNumLabels int, wireConfig turbotunnel.WireConfig, forgedStats *ForgedStats, rrType uint16, queueSize int, overflowMode turbotunnel.QueueOverflowMode) *DNSPacketConn
NewDNSPacketConn creates a new DNSPacketConn. transport, through its WriteTo and ReadFrom methods, handles the actual sending and receiving the DNS messages encoded by DNSPacketConn. addr is the address to be passed to transport.WriteTo whenever a message needs to be sent. maxQnameLen is the max total QNAME length (0 = 253 per RFC 1035). maxNumLabels is the max number of data labels (0 = unlimited). forgedStats is shared with the transport layer (e.g. UDPPacketConn) for consistent forged response tracking; if nil, a new instance is created.
func (*DNSPacketConn) TransportErrors ¶
func (c *DNSPacketConn) TransportErrors() <-chan error
TransportErrors returns a channel that receives errors from the underlying transport goroutines (recvLoop and sendLoop).
type ForgedStats ¶ added in v0.2.1
ForgedStats tracks forged DNS response counters. It is shared between UDPPacketConn (per-query mode) and DNSPacketConn (shared socket mode) so that forged response visibility is consistent regardless of transport.
func (*ForgedStats) Record ¶ added in v0.2.1
func (s *ForgedStats) Record(rcode uint16)
Record increments the appropriate counter for the given RCODE and logs a summary at INFO level at milestone counts. Non-milestone forged responses are silently counted.
type HTTPPacketConn ¶
type HTTPPacketConn struct {
// QueuePacketConn is the direct receiver of ReadFrom and WriteTo calls.
// sendLoop, via send, removes messages from the outgoing queue that
// were placed there by WriteTo, and inserts messages into the incoming
// queue to be returned from ReadFrom.
*turbotunnel.QueuePacketConn
// contains filtered or unexported fields
}
HTTPPacketConn is an HTTP-based transport for DNS messages, used for DNS over HTTPS (DoH). Its WriteTo and ReadFrom methods exchange DNS messages over HTTP requests and responses.
HTTPPacketConn deals only with already formatted DNS messages. It does not handle encoding information into the messages. That is rather the responsibility of DNSPacketConn.
https://tools.ietf.org/html/rfc8484
func NewHTTPPacketConn ¶
func NewHTTPPacketConn(rt http.RoundTripper, urlString string, numSenders int, queueSize int, overflowMode turbotunnel.QueueOverflowMode) (*HTTPPacketConn, error)
NewHTTPPacketConn creates a new HTTPPacketConn configured to use the HTTP server at urlString as a DNS over HTTP resolver. client is the http.Client that will be used to make requests. urlString should include any necessary path components; e.g., "/dns-query". numSenders is the number of concurrent sender-receiver goroutines to run.
type Outbound ¶
type Outbound struct {
Resolvers []Resolver
TunnelServers []TunnelServer
// contains filtered or unexported fields
}
Outbound provides a high-level API for creating tunnels from multiple resolvers and tunnel servers.
func NewOutbound ¶
func NewOutbound(resolvers []Resolver, tunnelServers []TunnelServer) *Outbound
NewOutbound creates an Outbound with the given resolvers and tunnel servers.
type RateLimiter ¶
type RateLimiter struct {
// contains filtered or unexported fields
}
RateLimiter implements a token bucket rate limiter for DNS queries.
func NewRateLimiter ¶
func NewRateLimiter(rps float64) *RateLimiter
NewRateLimiter creates a new token bucket rate limiter with the given queries-per-second rate. Returns nil for non-positive or invalid values, which means unlimited.
func (*RateLimiter) Wait ¶
func (rl *RateLimiter) Wait()
Wait blocks until a token is available. It is safe to call on a nil receiver (no-op), which allows clean "unlimited" behavior without nil checks at call sites.
type Resolver ¶
type Resolver struct {
ResolverType ResolverType
ResolverAddr string // UDP: "1.1.1.1:53", DoT: "resolver:853", DoH: "https://resolver/dns-query"
// UTLSClientHelloID sets the uTLS fingerprint for DoH/DoT connections.
// nil means no uTLS (plain TLS).
UTLSClientHelloID *utls.ClientHelloID
// RoundTripper overrides the HTTP transport for DoH. If set,
// UTLSClientHelloID is ignored for DoH.
RoundTripper http.RoundTripper
// DialerControl is an optional callback for setting socket options
// (SO_MARK, SO_BINDTODEVICE, etc.) on UDP sockets.
DialerControl func(network, address string, c syscall.RawConn) error
// UDP transport settings (only apply to ResolverTypeUDP).
UDPWorkers int // concurrent UDP workers (0 = DefaultUDPWorkers)
UDPTimeout time.Duration // per-query response timeout (0 = DefaultUDPResponseTimeout)
UDPAcceptErrors bool // pass through non-NOERROR responses (default: filter)
}
Resolver holds DNS resolver configuration.
func NewResolver ¶
func NewResolver(resolverType ResolverType, resolverAddr string) (Resolver, error)
NewResolver creates a Resolver with the given type and address.
type ResolverType ¶
type ResolverType string
ResolverType identifies the DNS transport to use.
const ( ResolverTypeUDP ResolverType = "udp" ResolverTypeDOT ResolverType = "dot" ResolverTypeDOH ResolverType = "doh" )
type TLSPacketConn ¶
type TLSPacketConn struct {
// QueuePacketConn is the direct receiver of ReadFrom and WriteTo calls.
// recvLoop and sendLoop take the messages out of the receive and send
// queues and actually put them on the network.
*turbotunnel.QueuePacketConn
// contains filtered or unexported fields
}
TLSPacketConn is a TLS- and TCP-based transport for DNS messages, used for DNS over TLS (DoT). Its WriteTo and ReadFrom methods exchange DNS messages over a TLS channel, prefixing each message with a two-octet length field as in DNS over TCP.
TLSPacketConn deals only with already formatted DNS messages. It does not handle encoding information into the messages. That is rather the responsibility of DNSPacketConn.
https://tools.ietf.org/html/rfc7858
func NewTLSPacketConn ¶
func NewTLSPacketConn(addr string, dialTLSContext func(ctx context.Context, network, addr string) (net.Conn, error), queueSize int, overflowMode turbotunnel.QueueOverflowMode) (*TLSPacketConn, error)
NewTLSPacketConn creates a new TLSPacketConn configured to use the TLS server at addr as a DNS over TLS resolver. It maintains a TLS connection to the resolver, reconnecting as necessary. It closes the connection if any reconnection attempt fails.
func (*TLSPacketConn) Close ¶ added in v0.2.6
func (c *TLSPacketConn) Close() error
Close tears down both the packet queue and any active TLS connection, ensuring the reconnect goroutine exits.
type Tunnel ¶
type Tunnel struct {
Resolver Resolver
TunnelServer TunnelServer
// Session configuration. Zero values use defaults.
IdleTimeout time.Duration // default: 10s (2m with DnsttCompat)
KeepAlive time.Duration // default: 2s (10s with DnsttCompat)
OpenStreamTimeout time.Duration // default: 10s
MaxStreams int // default: 256 (0 = unlimited)
ReconnectMinDelay time.Duration // default: 1s
ReconnectMaxDelay time.Duration // default: 30s
SessionCheckInterval time.Duration // default: 20s
HandshakeTimeout time.Duration // default: 30s
PacketQueueSize int // default: QueueSize (512)
KCPWindowSize int // default: PacketQueueSize/2
QueueOverflowMode turbotunnel.QueueOverflowMode // default: drop
// contains filtered or unexported fields
}
Tunnel represents a DNS tunnel connection. Create with NewTunnel, then either call the step-by-step Initiate* methods (for embedding in frameworks like xray-core) or call ListenAndServe for a fully managed session.
func NewTunnel ¶
func NewTunnel(resolver Resolver, tunnelServer TunnelServer) (*Tunnel, error)
NewTunnel creates a Tunnel with the given resolver and server configuration. Zero-value fields use sensible defaults.
func (*Tunnel) InitiateDNSPacketConn ¶
InitiateDNSPacketConn wraps the resolver connection with DNS encoding.
func (*Tunnel) InitiateKCPConn ¶
InitiateKCPConn opens a KCP connection over the DNS packet connection. If mtu is 0, it is auto-computed from the domain and QNAME constraints.
func (*Tunnel) InitiateNoiseChannel ¶
InitiateNoiseChannel performs the Noise protocol handshake with a timeout. The timeout is controlled by HandshakeTimeout (default 30s).
func (*Tunnel) InitiateResolverConnection ¶
InitiateResolverConnection creates the underlying transport connection based on the Resolver configuration.
func (*Tunnel) InitiateSmuxSession ¶
InitiateSmuxSession establishes a multiplexed session over the Noise channel.
func (*Tunnel) ListenAndServe ¶
ListenAndServe starts a TCP listener and forwards connections through the tunnel with automatic session reconnection. This is the main entry point for the CLI.
type TunnelServer ¶
type TunnelServer struct {
Addr dns.Name
PubKey string
MTU int // auto-computed if 0 when InitiateKCPConn is called
// DnsttCompat enables the original dnstt wire format (8-byte ClientID,
// padding prefixes). When true, ClientIDSize is forced to 8.
DnsttCompat bool
// ClientIDSize is the ClientID size in bytes (default: 2).
// Ignored when DnsttCompat is true.
ClientIDSize int
// MaxQnameLen is the maximum QNAME wire length (default: 101, or 253 with DnsttCompat).
MaxQnameLen int
// MaxNumLabels is the maximum number of data labels (default: 0 = unlimited).
MaxNumLabels int
// RPS limits outgoing DNS queries per second (default: 0 = unlimited).
RPS float64
// RecordType selects the DNS record type for downstream data.
// Supported values: "txt" (default), "cname", "a", "aaaa", "mx", "ns", "srv".
RecordType string
// contains filtered or unexported fields
}
TunnelServer holds tunnel server configuration (domain + public key).
func NewTunnelServer ¶
func NewTunnelServer(addr string, pubKeyString string) (TunnelServer, error)
NewTunnelServer creates a TunnelServer from a domain string and hex-encoded public key.
type UDPPacketConn ¶
type UDPPacketConn struct {
*turbotunnel.QueuePacketConn
// contains filtered or unexported fields
}
UDPPacketConn implements net.PacketConn using per-query UDP sockets. Each outgoing DNS query is sent from a fresh socket with a random source port, making the tunnel harder to fingerprint and more resilient to censorship injection attacks that target a single source port.
A pool of worker goroutines dequeues packets from the embedded QueuePacketConn, sends each as a DNS query on a new socket, and reads the response back into the incoming queue. When ignoreErrors is true (default), workers skip non-NOERROR responses and keep reading until a valid response arrives or the per-query timeout expires — this defeats forged error injection by censors.