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
38 // base32Encoding is a base32 encoding without padding.
39 var base32Encoding
= base32
.StdEncoding
.WithPadding(base32
.NoPadding
)
41 // DNSPacketConn provides a packet-sending and -receiving interface over various
42 // forms of DNS. It handles the details of how packets and padding are encoded
43 // as a DNS name in the Question section of an upstream query, and as a TXT RR
44 // in downstream responses.
46 // DNSPacketConn does not handle the mechanics of actually sending and receiving
47 // encoded DNS messages. That is rather the responsibility of some other
48 // net.PacketConn such as net.UDPConn, HTTPPacketConn, or TLSPacketConn, one of
49 // which must be provided to NewDNSPacketConn.
51 // We don't have a need to match up a query and a response by ID. Queries and
52 // responses are vehicles for carrying data and for our purposes don't need to
53 // be correlated. When sending a query, we generate a random ID, and when
54 // receiving a response, we ignore the ID.
55 type DNSPacketConn
struct {
56 clientID turbotunnel
.ClientID
58 // Sending on pollChan permits sendLoop to send an empty polling query.
59 // sendLoop also does its own polling according to a time schedule.
60 pollChan
chan struct{}
61 // QueuePacketConn is the direct receiver of ReadFrom and WriteTo calls.
62 // recvLoop and sendLoop take the messages out of the receive and send
63 // queues and actually put them on the network.
64 *turbotunnel
.QueuePacketConn
67 // NewDNSPacketConn creates a new DNSPacketConn. transport, through its WriteTo
68 // and ReadFrom methods, handles the actual sending and receiving the DNS
69 // messages encoded by DNSPacketConn. addr is the address to be passed to
70 // transport.WriteTo whenever a message needs to be sent.
71 func NewDNSPacketConn(transport net
.PacketConn
, addr net
.Addr
, domain dns
.Name
) *DNSPacketConn
{
72 // Generate a new random ClientID.
73 clientID
:= turbotunnel
.NewClientID()
77 pollChan
: make(chan struct{}),
78 QueuePacketConn
: turbotunnel
.NewQueuePacketConn(clientID
, 0),
81 err
:= c
.recvLoop(transport
)
83 log
.Printf("recvLoop: %v", err
)
87 err
:= c
.sendLoop(transport
, addr
)
89 log
.Printf("sendLoop: %v", err
)
95 // dnsResponsePayload extracts the downstream payload of a DNS response, encoded
96 // into the RDATA of a TXT RR. It returns nil if the message doesn't pass format
97 // checks, or if the name in its Question entry is not a subdomain of domain.
98 func dnsResponsePayload(resp
*dns
.Message
, domain dns
.Name
) []byte {
99 if resp
.Flags
&0x8000 != 0x8000 {
100 // QR != 1, this is not a response.
103 if resp
.Flags
&0x000f != dns
.RcodeNoError
{
107 if len(resp
.Answer
) != 1 {
110 answer
:= resp
.Answer
[0]
112 _
, ok
:= answer
.Name
.TrimSuffix(domain
)
114 // Not the name we are expecting.
118 if answer
.Type
!= dns
.RRTypeTXT
{
119 // We only support TYPE == TXT.
122 payload
, err
:= dns
.DecodeRDataTXT(answer
.Data
)
130 // nextPacket reads the next length-prefixed packet from r. It returns a nil
131 // error only when a complete packet was read. It returns io.EOF only when there
132 // were 0 bytes remaining to read from r. It returns io.ErrUnexpectedEOF when
133 // EOF occurs in the middle of an encoded packet.
134 func nextPacket(r
*bytes
.Reader
) ([]byte, error
) {
137 err
:= binary
.Read(r
, binary
.BigEndian
, &n
)
139 // We may return a real io.EOF only here.
143 _
, err
= io
.ReadFull(r
, p
)
144 // Here we must change io.EOF to io.ErrUnexpectedEOF.
146 err
= io
.ErrUnexpectedEOF
152 // recvLoop repeatedly calls transport.ReadFrom to receive a DNS message,
153 // extracts its payload and breaks it into packets, and stores the packets in a
154 // queue to be returned from a future call to c.ReadFrom.
156 // Whenever we receive a response with a non-empty payload, we send twice on
157 // c.pollChan to permit sendLoop to send two immediate polling queries. The
158 // intuition behind polling immediately after receiving is that we know the
159 // server has just had something to send, it may need to send more, and the only
160 // way it can send is if we give it a query to respond to. The intuition behind
161 // doing *two* polls when we receive is similar to TCP slow start: we want to
162 // maintain some number of queries "in flight", and the faster the server is
163 // sending, the higher that number should be. If we polled only once in response
164 // to received data, we would tend to have only one query in flight at a time,
165 // ping-pong style. The first polling request replaces the in-flight request
166 // that has just finished in our receiving data; the second grows the effective
167 // in-flight window proportionally to the rate at which data-carrying responses
168 // are being received. Compare to Eq. (2) of
169 // https://tools.ietf.org/html/rfc5681#section-3.1; the differences are that we
170 // count messages, not bytes, and we don't maintain an explicit window. If a
171 // response comes back without data, or if a query or response is dropped by the
172 // network, then we don't poll again, which decreases the effective in-flight
174 func (c
*DNSPacketConn
) recvLoop(transport net
.PacketConn
) error
{
177 n
, addr
, err
:= transport
.ReadFrom(buf
[:])
179 if err
, ok
:= err
.(net
.Error
); ok
&& err
.Temporary() {
180 log
.Printf("ReadFrom temporary error: %v", err
)
186 // Got a response. Try to parse it as a DNS message.
187 resp
, err
:= dns
.MessageFromWireFormat(buf
[:n
])
189 log
.Printf("MessageFromWireFormat: %v", err
)
193 payload
:= dnsResponsePayload(&resp
, c
.domain
)
195 // Pull out the packets contained in the payload.
196 r
:= bytes
.NewReader(payload
)
199 p
, err
:= nextPacket(r
)
204 c
.QueuePacketConn
.QueueIncoming(p
, addr
)
207 // If the payload contained one or more packets, permit sendLoop
208 // to poll immediately.
211 case c
.pollChan
<- struct{}{}:
215 case c
.pollChan
<- struct{}{}:
222 // chunks breaks p into non-empty subslices of at most n bytes, greedily so that
223 // only final subslice has length < n.
224 func chunks(p
[]byte, n
int) [][]byte {
231 result
= append(result
, p
[:sz
])
237 // send sends p as a single packet encoded into a DNS query, using
238 // transport.WriteTo(query, addr). The length of p must be less than 224 bytes.
240 // Here is an example of how a packet is encoded into a DNS name, using
241 // p = "supercalifragilisticexpialidocious"
242 // c.clientID = "CLIENTID"
243 // domain = "t.example.com"
245 // 0. Start with the raw packet contents.
246 // supercalifragilisticexpialidocious
247 // 1. Length-prefix the packet and add random padding. A length prefix L < 0xe0
248 // means a data packet of L bytes. A length prefix L >= 0xe0 means padding of L -
249 // 0xe0 bytes (not counting the length of the length prefix itself).
250 // \xe3\xd9\xa3\x15\x22supercalifragilisticexpialidocious
251 // 2. Prefix the ClientID.
252 // CLIENTID\xe3\xd9\xa3\x15\x22supercalifragilisticexpialidocious
253 // 3. Base32-encode, without padding and in lower case.
254 // ingesrkokreujy6zumkse43vobsxey3bnruwm4tbm5uwy2ltoruwgzlyobuwc3djmrxwg2lpovzq
255 // 4. Break into labels of at most 63 octets.
256 // ingesrkokreujy6zumkse43vobsxey3bnruwm4tbm5uwy2ltoruwgzlyobuwc3d.jmrxwg2lpovzq
257 // 5. Append the domain.
258 // ingesrkokreujy6zumkse43vobsxey3bnruwm4tbm5uwy2ltoruwgzlyobuwc3d.jmrxwg2lpovzq.t.example.com
259 func (c
*DNSPacketConn
) send(transport net
.PacketConn
, p
[]byte, addr net
.Addr
) error
{
263 return fmt
.Errorf("too long")
267 buf
.Write(c
.clientID
[:])
270 n
= numPaddingForPoll
272 // Padding / cache inhibition
273 buf
.WriteByte(byte(224 + n
))
274 io
.CopyN(&buf
, rand
.Reader
, int64(n
))
277 buf
.WriteByte(byte(len(p
)))
280 decoded
= buf
.Bytes()
283 encoded
:= make([]byte, base32Encoding
.EncodedLen(len(decoded
)))
284 base32Encoding
.Encode(encoded
, decoded
)
285 encoded
= bytes
.ToLower(encoded
)
286 labels
:= chunks(encoded
, 63)
287 labels
= append(labels
, c
.domain
...)
288 name
, err
:= dns
.NewName(labels
)
294 binary
.Read(rand
.Reader
, binary
.BigEndian
, &id
)
295 query
:= &dns
.Message
{
297 Flags
: 0x0100, // QR = 0, RD = 1
298 Question
: []dns
.Question
{
306 Additional
: []dns
.RR
{
310 Class
: 4096, // requester's UDP payload size
311 TTL
: 0, // extended RCODE and flags
316 buf
, err
:= query
.WireFormat()
321 _
, err
= transport
.WriteTo(buf
, addr
)
325 // sendLoop takes packets that have been written using c.WriteTo, and sends them
326 // on the network using send. It also does polling with empty packets when
327 // requested by pollChan or after a timeout.
328 func (c
*DNSPacketConn
) sendLoop(transport net
.PacketConn
, addr net
.Addr
) error
{
329 pollDelay
:= initPollDelay
330 pollTimer
:= time
.NewTimer(pollDelay
)
333 outgoingQueue
:= c
.QueuePacketConn
.OutgoingQueue(addr
)
334 pollTimerExpired
:= false
335 // Prioritize sending an actual data packet from OutgoingQueue.
336 // Only consider a poll when OutgoingQueue is empty.
338 case p
= <-outgoingQueue
:
341 case p
= <-outgoingQueue
:
346 pollTimerExpired
= true
351 // A data-carrying packet displaces one pending poll
352 // opportunity, if any.
359 if pollTimerExpired
{
360 // We're polling because it's been a while since we last
361 // polled. Increase the poll delay.
362 pollDelay
= time
.Duration(float64(pollDelay
) * pollDelayMultiplier
)
363 if pollDelay
> maxPollDelay
{
364 pollDelay
= maxPollDelay
367 // We're sending an actual data packet, or we're polling
368 // in response to a received packet. Reset the poll
370 if !pollTimer
.Stop() {
373 pollDelay
= initPollDelay
375 pollTimer
.Reset(pollDelay
)
377 // Unlike in the server, in the client we assume that because
378 // the data capacity of queries is so limited, it's not worth
379 // trying to send more than one packet per query.
380 err
:= c
.send(transport
, p
, addr
)
382 log
.Printf("send: %v", err
)