mirror of
https://github.com/tladesignz/dnstt.git
synced 2026-09-22 23:07:59 +03:00
Moved most files of the client into a subdirectory, to reduce need for patching for use in IPtProxy.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package dnstt_client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -16,8 +16,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// How many bytes of random padding to insert into queries.
|
||||
numPadding = 3
|
||||
// NumPadding How many bytes of random padding to insert into queries.
|
||||
NumPadding = 3
|
||||
// In an otherwise empty polling query, insert even more random padding,
|
||||
// to reduce the chance of a cache hit. Cannot be greater than 31,
|
||||
// because the prefix codes indicating padding start at 224.
|
||||
@@ -39,8 +39,8 @@ const (
|
||||
pollLimit = 16
|
||||
)
|
||||
|
||||
// base32Encoding is a base32 encoding without padding.
|
||||
var base32Encoding = base32.StdEncoding.WithPadding(base32.NoPadding)
|
||||
// Base32Encoding is a base32 encoding without padding.
|
||||
var Base32Encoding = base32.StdEncoding.WithPadding(base32.NoPadding)
|
||||
|
||||
// DNSPacketConn provides a packet-sending and -receiving interface over various
|
||||
// forms of DNS. It handles the details of how packets and padding are encoded
|
||||
@@ -187,6 +187,7 @@ func (c *DNSPacketConn) recvLoop(transport net.PacketConn) error {
|
||||
var buf [4096]byte
|
||||
n, addr, err := transport.ReadFrom(buf[:])
|
||||
if err != nil {
|
||||
//goland:noinspection GoDeprecation
|
||||
if err, ok := err.(net.Error); ok && err.Temporary() {
|
||||
log.Printf("ReadFrom temporary error: %v", err)
|
||||
continue
|
||||
@@ -205,13 +206,13 @@ func (c *DNSPacketConn) recvLoop(transport net.PacketConn) error {
|
||||
|
||||
// Pull out the packets contained in the payload.
|
||||
r := bytes.NewReader(payload)
|
||||
any := false
|
||||
anyPacket := false
|
||||
for {
|
||||
p, err := nextPacket(r)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
any = true
|
||||
anyPacket = true
|
||||
c.QueuePacketConn.QueueIncoming(p, addr)
|
||||
}
|
||||
|
||||
@@ -219,7 +220,7 @@ func (c *DNSPacketConn) recvLoop(transport net.PacketConn) error {
|
||||
// to poll immediately. ACKs on received data will effectively
|
||||
// serve as another stream of polls whose rate is proportional
|
||||
// to the rate of incoming packets.
|
||||
if any {
|
||||
if anyPacket {
|
||||
select {
|
||||
case c.pollChan <- struct{}{}:
|
||||
default:
|
||||
@@ -228,9 +229,9 @@ func (c *DNSPacketConn) recvLoop(transport net.PacketConn) error {
|
||||
}
|
||||
}
|
||||
|
||||
// chunks breaks p into non-empty subslices of at most n bytes, greedily so that
|
||||
// Chunks breaks p into non-empty subslices of at most n bytes, greedily so that
|
||||
// only final subslice has length < n.
|
||||
func chunks(p []byte, n int) [][]byte {
|
||||
func Chunks(p []byte, n int) [][]byte {
|
||||
var result [][]byte
|
||||
for len(p) > 0 {
|
||||
sz := len(p)
|
||||
@@ -274,13 +275,13 @@ func (c *DNSPacketConn) send(transport net.PacketConn, p []byte, addr net.Addr)
|
||||
var buf bytes.Buffer
|
||||
// ClientID
|
||||
buf.Write(c.clientID[:])
|
||||
n := numPadding
|
||||
n := NumPadding
|
||||
if len(p) == 0 {
|
||||
n = numPaddingForPoll
|
||||
}
|
||||
// Padding / cache inhibition
|
||||
buf.WriteByte(byte(224 + n))
|
||||
io.CopyN(&buf, rand.Reader, int64(n))
|
||||
_, _ = io.CopyN(&buf, rand.Reader, int64(n))
|
||||
// Packet contents
|
||||
if len(p) > 0 {
|
||||
buf.WriteByte(byte(len(p)))
|
||||
@@ -289,10 +290,10 @@ func (c *DNSPacketConn) send(transport net.PacketConn, p []byte, addr net.Addr)
|
||||
decoded = buf.Bytes()
|
||||
}
|
||||
|
||||
encoded := make([]byte, base32Encoding.EncodedLen(len(decoded)))
|
||||
base32Encoding.Encode(encoded, decoded)
|
||||
encoded := make([]byte, Base32Encoding.EncodedLen(len(decoded)))
|
||||
Base32Encoding.Encode(encoded, decoded)
|
||||
encoded = bytes.ToLower(encoded)
|
||||
labels := chunks(encoded, 63)
|
||||
labels := Chunks(encoded, 63)
|
||||
labels = append(labels, c.domain...)
|
||||
name, err := dns.NewName(labels)
|
||||
if err != nil {
|
||||
@@ -300,7 +301,7 @@ func (c *DNSPacketConn) send(transport net.PacketConn, p []byte, addr net.Addr)
|
||||
}
|
||||
|
||||
var id uint16
|
||||
binary.Read(rand.Reader, binary.BigEndian, &id)
|
||||
_ = binary.Read(rand.Reader, binary.BigEndian, &id)
|
||||
query := &dns.Message{
|
||||
ID: id,
|
||||
Flags: 0x0100, // QR = 0, RD = 1
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package dnstt_client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package dnstt_client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -88,7 +88,9 @@ func (c *HTTPPacketConn) send(p []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package dnstt_client
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package dnstt_client
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@@ -54,7 +54,9 @@ func NewTLSPacketConn(addr string, dialTLSContext func(ctx context.Context, netw
|
||||
QueuePacketConn: turbotunnel.NewQueuePacketConn(turbotunnel.DummyAddr{}, 0),
|
||||
}
|
||||
go func() {
|
||||
defer c.Close()
|
||||
defer func() {
|
||||
_ = c.Close()
|
||||
}()
|
||||
for {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
@@ -73,7 +75,7 @@ func NewTLSPacketConn(addr string, dialTLSContext func(ctx context.Context, netw
|
||||
wg.Done()
|
||||
}()
|
||||
wg.Wait()
|
||||
conn.Close()
|
||||
_ = conn.Close()
|
||||
|
||||
// Whenever the TLS connection dies, redial a new one.
|
||||
conn, err = dial()
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package dnstt_client
|
||||
|
||||
// Support code for TLS camouflage using uTLS.
|
||||
|
||||
@@ -16,9 +16,9 @@ import (
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
// utlsClientHelloIDMap is a correspondence between human-readable labels and
|
||||
// UtlsClientHelloIDMap is a correspondence between human-readable labels and
|
||||
// supported utls.ClientHelloIDs.
|
||||
var utlsClientHelloIDMap = []struct {
|
||||
var UtlsClientHelloIDMap = []struct {
|
||||
Label string
|
||||
ID *utls.ClientHelloID
|
||||
}{
|
||||
@@ -38,10 +38,10 @@ var utlsClientHelloIDMap = []struct {
|
||||
{"iOS_12_1", &utls.HelloIOS_12_1},
|
||||
}
|
||||
|
||||
// utlsLookup returns a *utls.ClientHelloID from utlsClientHelloIDMap by a
|
||||
// UtlsLookup returns a *utls.ClientHelloID from utlsClientHelloIDMap by a
|
||||
// case-insensitive label match, or nil if there is no match.
|
||||
func utlsLookup(label string) *utls.ClientHelloID {
|
||||
for _, entry := range utlsClientHelloIDMap {
|
||||
func UtlsLookup(label string) *utls.ClientHelloID {
|
||||
for _, entry := range UtlsClientHelloIDMap {
|
||||
if strings.ToLower(label) == strings.ToLower(entry.Label) {
|
||||
return entry.ID
|
||||
}
|
||||
@@ -49,10 +49,10 @@ func utlsLookup(label string) *utls.ClientHelloID {
|
||||
return nil
|
||||
}
|
||||
|
||||
// utlsDialContext connects to the given network address and initiates a TLS
|
||||
// UtlsDialContext connects to the given network address and initiates a TLS
|
||||
// handshake with the provided ClientHelloID, and returns the resulting TLS
|
||||
// connection.
|
||||
func utlsDialContext(ctx context.Context, network, addr string, config *utls.Config, id *utls.ClientHelloID) (*utls.UConn, error) {
|
||||
func UtlsDialContext(ctx context.Context, network, addr string, config *utls.Config, id *utls.ClientHelloID) (*utls.UConn, error) {
|
||||
// Set the SNI from addr, if not already set.
|
||||
if config == nil {
|
||||
config = &utls.Config{}
|
||||
@@ -76,7 +76,7 @@ func utlsDialContext(ctx context.Context, network, addr string, config *utls.Con
|
||||
if net.ParseIP(config.ServerName) != nil {
|
||||
err := uconn.RemoveSNIExtension()
|
||||
if err != nil {
|
||||
uconn.Close()
|
||||
_ = uconn.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,7 @@ func utlsDialContext(ctx context.Context, network, addr string, config *utls.Con
|
||||
// https://github.com/refraction-networking/utls/issues/75
|
||||
err = uconn.Handshake()
|
||||
if err != nil {
|
||||
uconn.Close()
|
||||
_ = uconn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return uconn, nil
|
||||
@@ -186,7 +186,7 @@ func makeRoundTripper(req *http.Request, config *utls.Config, id *utls.ClientHel
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bootstrapConn, err := utlsDialContext(req.Context(), "tcp", addr, config, id)
|
||||
bootstrapConn, err := UtlsDialContext(req.Context(), "tcp", addr, config, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -210,7 +210,7 @@ func makeRoundTripper(req *http.Request, config *utls.Config, id *utls.ClientHel
|
||||
}
|
||||
|
||||
// Later dials make a new connection.
|
||||
uconn, err := utlsDialContext(ctx, "tcp", addr, config, id)
|
||||
uconn, err := UtlsDialContext(ctx, "tcp", addr, config, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package dnstt_client
|
||||
|
||||
// Random selection from weighted distributions, and strings for specifying such
|
||||
// distributions.
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// parseWeightedList parses a list of text labels with optional numeric weights,
|
||||
// ParseWeightedList parses a list of text labels with optional numeric weights,
|
||||
// and returns parallel slices of weights and labels. If a weight is omitted for
|
||||
// a label, the weight is 1.
|
||||
//
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
//
|
||||
// list ::= entry ("," entry)*
|
||||
// entry ::= (weight "*")? label
|
||||
func parseWeightedList(s string) ([]uint32, []string, error) {
|
||||
func ParseWeightedList(s string) ([]uint32, []string, error) {
|
||||
const (
|
||||
kindEOF = iota
|
||||
kindComma
|
||||
@@ -176,10 +176,10 @@ func (s cryptoSource) Int63() int64 {
|
||||
return n
|
||||
}
|
||||
|
||||
// sampleWeighted returns the index of a randomly selected element of the
|
||||
// SampleWeighted returns the index of a randomly selected element of the
|
||||
// weights slice, weighted by the values stored in the slice. Panics if
|
||||
// the sum of the weights is zero or does not fit in an int64.
|
||||
func sampleWeighted(weights []uint32) int {
|
||||
func SampleWeighted(weights []uint32) int {
|
||||
var sum int64 = 0
|
||||
for _, w := range weights {
|
||||
sum += int64(w)
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package dnstt_client
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -20,7 +20,7 @@ func TestParseWeightedList(t *testing.T) {
|
||||
{"\\,", []uint32{1}, []string{","}},
|
||||
{"3\\*apple\\,car\\rot,100*orange", []uint32{1, 100}, []string{"3*apple,carrot", "orange"}},
|
||||
} {
|
||||
weights, labels, err := parseWeightedList(test.input)
|
||||
weights, labels, err := ParseWeightedList(test.input)
|
||||
if err != nil {
|
||||
t.Errorf("%+q resulted in error: %v", test.input, err)
|
||||
continue
|
||||
@@ -55,7 +55,7 @@ func TestParseWeightedList(t *testing.T) {
|
||||
"-5*apple",
|
||||
"5.5*apple",
|
||||
} {
|
||||
_, _, err := parseWeightedList(input)
|
||||
_, _, err := ParseWeightedList(input)
|
||||
if err == nil {
|
||||
t.Errorf("%+q resulted in no error", input)
|
||||
continue
|
||||
@@ -77,7 +77,7 @@ func TestSampleWeighted(t *testing.T) {
|
||||
t.Errorf("%v: expected panic", weights)
|
||||
}
|
||||
}()
|
||||
sampleWeighted(weights)
|
||||
SampleWeighted(weights)
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ func TestSampleWeighted(t *testing.T) {
|
||||
{[]uint32{0, 0, 0xffffffff, 0, 1}, 2},
|
||||
} {
|
||||
for i := 0; i < 100; i++ {
|
||||
index := sampleWeighted(test.weights)
|
||||
index := SampleWeighted(test.weights)
|
||||
if index != test.index {
|
||||
t.Errorf("%v: expected %d, got %d", test.weights, test.index, index)
|
||||
}
|
||||
+13
-11
@@ -48,6 +48,7 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
dc "www.bamsoftware.com/git/dnstt.git/dnstt-client/lib"
|
||||
|
||||
utls "github.com/refraction-networking/utls"
|
||||
"github.com/xtaci/kcp-go/v5"
|
||||
@@ -99,7 +100,7 @@ func readKeyFromFile(filename string) ([]byte, error) {
|
||||
// utls.ClientHelloID from utlsClientHelloIDMap, and randomly samples one
|
||||
// utls.ClientHelloID from the distribution.
|
||||
func sampleUTLSDistribution(spec string) (*utls.ClientHelloID, error) {
|
||||
weights, labels, err := parseWeightedList(spec)
|
||||
weights, labels, err := dc.ParseWeightedList(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -109,14 +110,14 @@ func sampleUTLSDistribution(spec string) (*utls.ClientHelloID, error) {
|
||||
if label == "none" {
|
||||
id = nil
|
||||
} else {
|
||||
id = utlsLookup(label)
|
||||
id = dc.UtlsLookup(label)
|
||||
if id == nil {
|
||||
return nil, fmt.Errorf("unknown TLS fingerprint %q", label)
|
||||
}
|
||||
}
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return ids[sampleWeighted(weights)], nil
|
||||
return ids[dc.SampleWeighted(weights)], nil
|
||||
}
|
||||
|
||||
func handle(local *net.TCPConn, sess *smux.Session, conv uint32) error {
|
||||
@@ -170,7 +171,7 @@ func listen(pubkey []byte, domain dns.Name, remoteAddr net.Addr, pconn net.Packe
|
||||
return nil, nil, nil, fmt.Errorf("opening local listener: %v", err)
|
||||
}
|
||||
|
||||
mtu := dnsNameCapacity(domain) - 8 - 1 - numPadding - 1 // clientid + padding length prefix + padding + data length prefix
|
||||
mtu := dnsNameCapacity(domain) - 8 - 1 - dc.NumPadding - 1 // clientid + padding length prefix + padding + data length prefix
|
||||
if mtu < 80 {
|
||||
_ = pconn.Close()
|
||||
_ = ln.Close()
|
||||
@@ -244,6 +245,7 @@ func acceptLoop(ln *pt.SocksListener, pconn net.PacketConn, conn *kcp.UDPSession
|
||||
for {
|
||||
local, err := ln.AcceptSocks()
|
||||
if err != nil {
|
||||
//goland:noinspection GoDeprecation
|
||||
if err, ok := err.(net.Error); ok && err.Temporary() {
|
||||
continue
|
||||
}
|
||||
@@ -308,9 +310,9 @@ Examples:
|
||||
|
||||
`, os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
labels := make([]string, 0, len(utlsClientHelloIDMap))
|
||||
labels := make([]string, 0, len(dc.UtlsClientHelloIDMap))
|
||||
labels = append(labels, "none")
|
||||
for _, entry := range utlsClientHelloIDMap {
|
||||
for _, entry := range dc.UtlsClientHelloIDMap {
|
||||
labels = append(labels, entry.Label)
|
||||
}
|
||||
_, _ = fmt.Fprintf(flag.CommandLine.Output(), `
|
||||
@@ -407,9 +409,9 @@ Known TLS fingerprints for -utls are:
|
||||
transport.Proxy = nil
|
||||
rt = transport
|
||||
} else {
|
||||
rt = NewUTLSRoundTripper(nil, utlsClientHelloID)
|
||||
rt = dc.NewUTLSRoundTripper(nil, utlsClientHelloID)
|
||||
}
|
||||
pconn, err := NewHTTPPacketConn(rt, dohURL, 32)
|
||||
pconn, err := dc.NewHTTPPacketConn(rt, dohURL, 32)
|
||||
return addr, pconn, err
|
||||
}},
|
||||
// -dot
|
||||
@@ -420,10 +422,10 @@ Known TLS fingerprints for -utls are:
|
||||
dialTLSContext = (&tls.Dialer{}).DialContext
|
||||
} else {
|
||||
dialTLSContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return utlsDialContext(ctx, network, addr, nil, utlsClientHelloID)
|
||||
return dc.UtlsDialContext(ctx, network, addr, nil, utlsClientHelloID)
|
||||
}
|
||||
}
|
||||
pconn, err := NewTLSPacketConn(dotAddr, dialTLSContext)
|
||||
pconn, err := dc.NewTLSPacketConn(dotAddr, dialTLSContext)
|
||||
return addr, pconn, err
|
||||
}},
|
||||
// -udp
|
||||
@@ -473,7 +475,7 @@ Known TLS fingerprints for -utls are:
|
||||
for _, methodName := range ptInfo.MethodNames {
|
||||
switch methodName {
|
||||
case "dnstt":
|
||||
pconn = NewDNSPacketConn(pconn, remoteAddr, domain)
|
||||
pconn = dc.NewDNSPacketConn(pconn, remoteAddr, domain)
|
||||
ln, conn, sess, err := listen(pubkey, domain, remoteAddr, pconn)
|
||||
if err != nil {
|
||||
_ = pt.CmethodError(methodName, err.Error())
|
||||
|
||||
@@ -3,13 +3,14 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
dc "www.bamsoftware.com/git/dnstt.git/dnstt-client/lib"
|
||||
|
||||
"www.bamsoftware.com/git/dnstt.git/dns"
|
||||
)
|
||||
|
||||
func TestDNSNameCapacity(t *testing.T) {
|
||||
for domainLen := 0; domainLen < 255; domainLen++ {
|
||||
domain, err := dns.NewName(chunks(bytes.Repeat([]byte{'x'}, domainLen), 63))
|
||||
domain, err := dns.NewName(dc.Chunks(bytes.Repeat([]byte{'x'}, domainLen), 63))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -17,8 +18,8 @@ func TestDNSNameCapacity(t *testing.T) {
|
||||
if capacity <= 0 {
|
||||
continue
|
||||
}
|
||||
prefix := []byte(base32Encoding.EncodeToString(bytes.Repeat([]byte{'y'}, capacity)))
|
||||
labels := append(chunks(prefix, 63), domain...)
|
||||
prefix := []byte(dc.Base32Encoding.EncodeToString(bytes.Repeat([]byte{'y'}, capacity)))
|
||||
labels := append(dc.Chunks(prefix, 63), domain...)
|
||||
_, err = dns.NewName(labels)
|
||||
if err != nil {
|
||||
t.Errorf("length %v capacity %v %v", domainLen, capacity, err)
|
||||
|
||||
Reference in New Issue
Block a user