@@ -21,6 +21,7 @@ import (
2121var (
2222 ErrTXTAnswerMissing = errors .New ("dns txt answer missing" )
2323 ErrTXTAnswerMalformed = errors .New ("dns txt answer malformed" )
24+ ErrTXTAnswerTooLarge = errors .New ("dns txt answer too large" )
2425)
2526
2627const (
@@ -89,8 +90,13 @@ func BuildTXTResponsePacket(questionPacket []byte, answerName string, answerPayl
8990 }
9091
9192 answerLen := 0
92- for _ , payload := range answerPayloads {
93- answerLen += len (nameBytes ) + 10 + len (payload )
93+ useAnswerNameCompression := len (answerPayloads ) > 1
94+ for i , payload := range answerPayloads {
95+ nameLen := len (nameBytes )
96+ if useAnswerNameCompression && i > 0 {
97+ nameLen = 2
98+ }
99+ answerLen += nameLen + 10 + len (payload )
94100 }
95101
96102 response := make ([]byte , dnsHeaderSize + len (questionBytes )+ answerLen + rawRecordsLen (optRecords ))
@@ -103,9 +109,15 @@ func BuildTXTResponsePacket(questionPacket []byte, answerName string, answerPayl
103109
104110 offset := dnsHeaderSize
105111 offset += copy (response [offset :], questionBytes )
112+ firstAnswerNameOffset := offset
106113
107- for _ , payload := range answerPayloads {
108- offset += copy (response [offset :], nameBytes )
114+ for i , payload := range answerPayloads {
115+ if useAnswerNameCompression && i > 0 && firstAnswerNameOffset <= 0x3FFF {
116+ binary .BigEndian .PutUint16 (response [offset :offset + 2 ], uint16 (0xC000 | firstAnswerNameOffset ))
117+ offset += 2
118+ } else {
119+ offset += copy (response [offset :], nameBytes )
120+ }
109121 binary .BigEndian .PutUint16 (response [offset :offset + 2 ], enums .DNSRecordTypeTXT )
110122 binary .BigEndian .PutUint16 (response [offset + 2 :offset + 4 ], enums .DNSQClassIN )
111123 binary .BigEndian .PutUint32 (response [offset + 4 :offset + 8 ], 0 )
@@ -137,7 +149,10 @@ func BuildVPNResponsePacket(questionPacket []byte, answerName string, packet vpn
137149 return nil , err
138150 }
139151
140- answerPayloads := buildTXTAnswerChunks (rawFrame , baseEncode )
152+ answerPayloads , err := buildTXTAnswerChunks (rawFrame , baseEncode )
153+ if err != nil {
154+ return nil , err
155+ }
141156 return BuildTXTResponsePacket (questionPacket , answerName , answerPayloads )
142157}
143158
@@ -212,14 +227,14 @@ func BuildTunnelQuestionName(domain string, encodedFrame string) (string, error)
212227 return name , nil
213228}
214229
215- func buildTXTAnswerChunks (rawFrame []byte , baseEncode bool ) [][]byte {
230+ func buildTXTAnswerChunks (rawFrame []byte , baseEncode bool ) ( [][]byte , error ) {
216231 maxChunk := maxTXTAnswerPayload
217232 if baseEncode {
218233 maxChunk = maxTXTEncodedChunk
219234 }
220235
221236 if len (rawFrame ) == 0 {
222- return [][]byte {appendLengthPrefixedTXT (nil )}
237+ return [][]byte {appendLengthPrefixedTXT (nil )}, nil
223238 }
224239
225240 encodeChunk := func (raw []byte ) []byte {
@@ -231,12 +246,12 @@ func buildTXTAnswerChunks(rawFrame []byte, baseEncode bool) [][]byte {
231246 }
232247
233248 if len (rawFrame ) <= maxChunk {
234- return [][]byte {encodeChunk (rawFrame )}
249+ return [][]byte {encodeChunk (rawFrame )}, nil
235250 }
236251
237252 header , err := vpnproto .Parse (rawFrame )
238253 if err != nil {
239- return [][]byte {encodeChunk (rawFrame )}
254+ return [][]byte {encodeChunk (rawFrame )}, nil
240255 }
241256
242257 headerLen := header .HeaderLength
@@ -252,6 +267,9 @@ func buildTXTAnswerChunks(rawFrame []byte, baseEncode bool) [][]byte {
252267 if remaining > 0 {
253268 totalChunks += (remaining + maxChunkNData - 1 ) / maxChunkNData
254269 }
270+ if totalChunks > 255 {
271+ return nil , ErrTXTAnswerTooLarge
272+ }
255273
256274 chunks := make ([][]byte , 0 , totalChunks )
257275 rawChunk0 := make ([]byte , 0 , 2 + len (rawFrame ))
@@ -273,7 +291,7 @@ func buildTXTAnswerChunks(rawFrame []byte, baseEncode bool) [][]byte {
273291 cursor = end
274292 }
275293
276- return chunks
294+ return chunks , nil
277295}
278296
279297func appendLengthPrefixedTXT (data []byte ) []byte {
@@ -351,8 +369,9 @@ func assembleVPNResponse(rawAnswers [][]byte, baseEncoded bool) (vpnproto.Packet
351369 return vpnproto .Parse (raw )
352370 }
353371
354- chunks := make ( map [ int ][]byte , len ( rawAnswers ))
372+ var chunks [ 256 ][]byte
355373 totalExpected := 0
374+ seenChunks := 0
356375 var header vpnproto.Packet
357376 headerSeen := false
358377
@@ -373,35 +392,53 @@ func assembleVPNResponse(rawAnswers [][]byte, baseEncoded bool) (vpnproto.Packet
373392 return vpnproto.Packet {}, ErrTXTAnswerMalformed
374393 }
375394 totalExpected = int (raw [1 ])
395+ if totalExpected <= 0 || totalExpected > len (chunks ) {
396+ return vpnproto.Packet {}, ErrTXTAnswerMalformed
397+ }
376398 parsed , err := vpnproto .ParseAtOffset (raw , 2 )
377399 if err != nil {
378400 return vpnproto.Packet {}, err
379401 }
380402 header = parsed
381403 headerSeen = true
404+ if chunks [0 ] == nil {
405+ seenChunks ++
406+ }
382407 chunks [0 ] = parsed .Payload
383408 continue
384409 }
385410
386- chunks [int (raw [0 ])] = raw [1 :]
411+ chunkID := int (raw [0 ])
412+ if chunkID >= len (chunks ) {
413+ return vpnproto.Packet {}, ErrTXTAnswerMalformed
414+ }
415+ if chunks [chunkID ] == nil {
416+ seenChunks ++
417+ }
418+ chunks [chunkID ] = raw [1 :]
387419 }
388420
389- if ! headerSeen || totalExpected <= 0 || len ( chunks ) != totalExpected {
421+ if ! headerSeen || totalExpected <= 0 || seenChunks != totalExpected {
390422 return vpnproto.Packet {}, ErrTXTAnswerMalformed
391423 }
392- for i := 0 ; i < totalExpected ; i ++ {
393- if _ , ok := chunks [i ]; ! ok {
424+ for i := range totalExpected {
425+ if chunks [i ] == nil {
426+ return vpnproto.Packet {}, ErrTXTAnswerMalformed
427+ }
428+ }
429+ for i := totalExpected ; i < len (chunks ); i ++ {
430+ if chunks [i ] != nil {
394431 return vpnproto.Packet {}, ErrTXTAnswerMalformed
395432 }
396433 }
397434
398435 payloadLen := 0
399- for i := 0 ; i < totalExpected ; i ++ {
436+ for i := range totalExpected {
400437 payloadLen += len (chunks [i ])
401438 }
402439
403440 payload := make ([]byte , 0 , payloadLen )
404- for i := 0 ; i < totalExpected ; i ++ {
441+ for i := range totalExpected {
405442 payload = append (payload , chunks [i ]... )
406443 }
407444 header .Payload = payload
0 commit comments