14 "www.bamsoftware.com/git/dnstt.git/dns"
15 "www.bamsoftware.com/git/dnstt.git/turbotunnel"
19 // How many bytes of random padding to insert into queries.
21 // In an otherwise empty polling query, insert even more random padding,
22 // to reduce the chance of a cache hit. Cannot be greater than 31,
23 // because the prefix codes indicating padding start at 224.
26 // sendLoop has a poll timer that automatically sends an empty polling
27 // query when a certain amount of time has elapsed without a send. The
28 // poll timer is initially set to initPollDelay. It increases by a
29 // factor of pollDelayMultiplier every time the poll timer expires, up
30 // to a maximum of maxPollDelay. The poll timer is reset to
31 // initPollDelay whenever an a send occurs that is not the result of the
32 // poll timer expiring.
33 initPollDelay
= 500 * time
.Millisecond
34 maxPollDelay
= 10 * time
.Second
35 pollDelayMultiplier
= 2.0
37 // A limit on the number of empty poll requests we may send in a burst
38 // as a result of receiving data.
42 // base32Encoding is a base32 encoding without padding.
43 var base32Encoding
= base32
.StdEncoding
.WithPadding(base32
.NoPadding
)
45 // DNSPacketConn provides a packet-sending and -receiving interface over various
46 // forms of DNS. It handles the details of how packets and padding are encoded
47 // as a DNS name in the Question section of an upstream query, and as a TXT RR
48 // in downstream responses.
50 // DNSPacketConn does not handle the mechanics of actually sending and receiving
51 // encoded DNS messages. That is rather the responsibility of some other
52 // net.PacketConn such as net.UDPConn, HTTPPacketConn, or TLSPacketConn, one of
53 // which must be provided to NewDNSPacketConn.
55 // We don't have a need to match up a query and a response by ID. Queries and
56 // responses are vehicles for carrying data and for our purposes don't need to
57 // be correlated. When sending a query, we generate a random ID, and when
58 // receiving a response, we ignore the ID.
59 type DNSPacketConn
struct {
60 clientID turbotunnel
.ClientID
62 // Sending on pollChan permits sendLoop to send an empty polling query.
63 // sendLoop also does its own polling according to a time schedule.
64 pollChan
chan struct{}
65 // QueuePacketConn is the direct receiver of ReadFrom and WriteTo calls.
66 // recvLoop and sendLoop take the messages out of the receive and send
67 // queues and actually put them on the network.
68 *turbotunnel
.QueuePacketConn
71 // NewDNSPacketConn creates a new DNSPacketConn. transport, through its WriteTo
72 // and ReadFrom methods, handles the actual sending and receiving the DNS
73 // messages encoded by DNSPacketConn. addr is the address to be passed to
74 // transport.WriteTo whenever a message needs to be sent.
75 func NewDNSPacketConn(transport net
.PacketConn
, addr net
.Addr
, domain dns
.Name
) *DNSPacketConn
{
76 // Generate a new random ClientID.
77 clientID
:= turbotunnel
.NewClientID()
81 pollChan
: make(chan struct{}, pollLimit
),
82 QueuePacketConn
: turbotunnel
.NewQueuePacketConn(clientID
, 0),
85 err
:= c
.recvLoop(transport
)
87 log
.Printf("recvLoop: %v", err
)
91 err
:= c
.sendLoop(transport
, addr
)
93 log
.Printf("sendLoop: %v", err
)
99 // dnsResponsePayload extracts the downstream payload of a DNS response, encoded
100 // into the RDATA of a TXT RR. It returns nil if the message doesn't pass format
101 // checks, or if the name in its Question entry is not a subdomain of domain.
102 func dnsResponsePayload(resp
*dns
.Message
, domain dns
.Name
) []byte {
103 if resp
.Flags
&0x8000 != 0x8000 {
104 // QR != 1, this is not a response.
107 if resp
.Flags
&0x000f != dns
.RcodeNoError
{
111 if len(resp
.Answer
) != 1 {
114 answer
:= resp
.Answer
[0]
116 _
, ok
:= answer
.Name
.TrimSuffix(domain
)
118 // Not the name we are expecting.
122 if answer
.Type
!= dns
.RRTypeTXT
{
123 // We only support TYPE == TXT.
126 payload
, err
:= dns
.DecodeRDataTXT(answer
.Data
)
134 // nextPacket reads the next length-prefixed packet from r. It returns a nil
135 // error only when a complete packet was read. It returns io.EOF only when there
136 // were 0 bytes remaining to read from r. It returns io.ErrUnexpectedEOF when
137 // EOF occurs in the middle of an encoded packet.
138 func nextPacket(r
*bytes
.Reader
) ([]byte, error
) {
141 err
:= binary
.Read(r
, binary
.BigEndian
, &n
)
143 // We may return a real io.EOF only here.
147 _
, err
= io
.ReadFull(r
, p
)
148 // Here we must change io.EOF to io.ErrUnexpectedEOF.
150 err
= io
.ErrUnexpectedEOF
156 // recvLoop repeatedly calls transport.ReadFrom to receive a DNS message,
157 // extracts its payload and breaks it into packets, and stores the packets in a
158 // queue to be returned from a future call to c.ReadFrom.
160 // Whenever we receive a DNS response containing at least one data packet, we
161 // send on c.pollChan to permit sendLoop to send an immediate polling queries.
162 // KCP itself will also send an ACK packet for incoming data, which is
163 // effectively a second poll. Therefore, each time we receive data, we send up
164 // to 2 polling queries (or 1 + f polling queries, if KCP only ACKs an f
165 // fraction of incoming data). We say "up to" because sendLoop will discard an
166 // empty polling query if it has an organic non-empty packet to send (this goes
167 // also for KCP's organic ACK packets).
169 // The intuition behind polling immediately after receiving is that if server
170 // has just had something to send, it may have more to send, and in order for
171 // the server to send anything, we must give it a query to respond to. The
172 // intuition behind polling *2 times* (or 1 + f times) is similar to TCP slow
173 // start: we want to maintain some number of queries "in flight", and the faster
174 // the server is sending, the higher that number should be. If we polled only
175 // once for each received packet, we would tend to have only one query in flight
176 // at a time, ping-pong style. The first polling query replaces the in-flight
177 // query that has just finished its duty in returning data to us; the second
178 // grows the effective in-flight window proportional to the rate at which
179 // data-carrying responses are being received. Compare to Eq. (2) of
180 // https://tools.ietf.org/html/rfc5681#section-3.1. The differences are that we
181 // count messages, not bytes, and we don't maintain an explicit window. If a
182 // response comes back without data, or if a query or response is dropped by the
183 // network, then we don't poll again, which decreases the effective in-flight
185 func (c
*DNSPacketConn
) recvLoop(transport net
.PacketConn
) error
{
188 n
, addr
, err
:= transport
.ReadFrom(buf
[:])
190 if err
, ok
:= err
.(net
.Error
); ok
&& err
.Temporary() {
191 log
.Printf("ReadFrom temporary error: %v", err
)
197 // Got a response. Try to parse it as a DNS message.
198 resp
, err
:= dns
.MessageFromWireFormat(buf
[:n
])
200 log
.Printf("MessageFromWireFormat: %v", err
)
204 payload
:= dnsResponsePayload(&resp
, c
.domain
)
206 // Pull out the packets contained in the payload.
207 r
:= bytes
.NewReader(payload
)
210 p
, err
:= nextPacket(r
)
215 c
.QueuePacketConn
.QueueIncoming(p
, addr
)
218 // If the payload contained one or more packets, permit sendLoop
219 // to poll immediately. ACKs on received data will effectively
220 // serve as another stream of polls whose rate is proportional
221 // to the rate of incoming packets.
224 case c
.pollChan
<- struct{}{}:
231 // chunks breaks p into non-empty subslices of at most n bytes, greedily so that
232 // only final subslice has length < n.
233 func chunks(p
[]byte, n
int) [][]byte {
240 result
= append(result
, p
[:sz
])
246 // send sends p as a single packet encoded into a DNS query, using
247 // transport.WriteTo(query, addr). The length of p must be less than 224 bytes.
249 // Here is an example of how a packet is encoded into a DNS name, using
251 // p = "supercalifragilisticexpialidocious"
252 // c.clientID = "CLIENTID"
253 // domain = "t.example.com"
257 // 0. Start with the raw packet contents.
259 // supercalifragilisticexpialidocious
261 // 1. Length-prefix the packet and add random padding. A length prefix L < 0xe0
262 // means a data packet of L bytes. A length prefix L ≥ 0xe0 means padding
263 // of L − 0xe0 bytes (not counting the length of the length prefix itself).
265 // \xe3\xd9\xa3\x15\x22supercalifragilisticexpialidocious
267 // 2. Prefix the ClientID.
269 // CLIENTID\xe3\xd9\xa3\x15\x22supercalifragilisticexpialidocious
271 // 3. Base32-encode, without padding and in lower case.
273 // ingesrkokreujy6zumkse43vobsxey3bnruwm4tbm5uwy2ltoruwgzlyobuwc3djmrxwg2lpovzq
275 // 4. Break into labels of at most 63 octets.
277 // ingesrkokreujy6zumkse43vobsxey3bnruwm4tbm5uwy2ltoruwgzlyobuwc3d.jmrxwg2lpovzq
279 // 5. Append the domain.
281 // ingesrkokreujy6zumkse43vobsxey3bnruwm4tbm5uwy2ltoruwgzlyobuwc3d.jmrxwg2lpovzq.t.example.com
282 func (c
*DNSPacketConn
) send(transport net
.PacketConn
, p
[]byte, addr net
.Addr
) error
{
286 return fmt
.Errorf("too long")
290 buf
.Write(c
.clientID
[:])
293 n
= numPaddingForPoll
295 // Padding / cache inhibition
296 buf
.WriteByte(byte(224 + n
))
297 io
.CopyN(&buf
, rand
.Reader
, int64(n
))
300 buf
.WriteByte(byte(len(p
)))
303 decoded
= buf
.Bytes()
306 encoded
:= make([]byte, base32Encoding
.EncodedLen(len(decoded
)))
307 base32Encoding
.Encode(encoded
, decoded
)
308 encoded
= bytes
.ToLower(encoded
)
309 labels
:= chunks(encoded
, 63)
310 labels
= append(labels
, c
.domain
...)
311 name
, err
:= dns
.NewName(labels
)
317 binary
.Read(rand
.Reader
, binary
.BigEndian
, &id
)
318 query
:= &dns
.Message
{
320 Flags
: 0x0100, // QR = 0, RD = 1
321 Question
: []dns
.Question
{
329 Additional
: []dns
.RR
{
333 Class
: 4096, // requester's UDP payload size
334 TTL
: 0, // extended RCODE and flags
339 buf
, err
:= query
.WireFormat()
344 _
, err
= transport
.WriteTo(buf
, addr
)
348 // sendLoop takes packets that have been written using c.WriteTo, and sends them
349 // on the network using send. It also does polling with empty packets when
350 // requested by pollChan or after a timeout.
351 func (c
*DNSPacketConn
) sendLoop(transport net
.PacketConn
, addr net
.Addr
) error
{
352 pollDelay
:= initPollDelay
353 pollTimer
:= time
.NewTimer(pollDelay
)
356 outgoing
:= c
.QueuePacketConn
.OutgoingQueue(addr
)
357 pollTimerExpired
:= false
358 // Prioritize sending an actual data packet from outgoing. Only
359 // consider a poll when outgoing is empty.
367 pollTimerExpired
= true
372 // A data-carrying packet displaces one pending poll
373 // opportunity, if any.
380 if pollTimerExpired
{
381 // We're polling because it's been a while since we last
382 // polled. Increase the poll delay.
383 pollDelay
= time
.Duration(float64(pollDelay
) * pollDelayMultiplier
)
384 if pollDelay
> maxPollDelay
{
385 pollDelay
= maxPollDelay
388 // We're sending an actual data packet, or we're polling
389 // in response to a received packet. Reset the poll
391 if !pollTimer
.Stop() {
394 pollDelay
= initPollDelay
396 pollTimer
.Reset(pollDelay
)
398 // Unlike in the server, in the client we assume that because
399 // the data capacity of queries is so limited, it's not worth
400 // trying to send more than one packet per query.
401 err
:= c
.send(transport
, p
, addr
)
403 log
.Printf("send: %v", err
)