gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

msg_truncate.go (3478B)


      1 package dns
      2 
      3 // Truncate ensures the reply message will fit into the requested buffer
      4 // size by removing records that exceed the requested size.
      5 //
      6 // It will first check if the reply fits without compression and then with
      7 // compression. If it won't fit with compression, Truncate then walks the
      8 // record adding as many records as possible without exceeding the
      9 // requested buffer size.
     10 //
     11 // If the message fits within the requested size without compression,
     12 // Truncate will set the message's Compress attribute to false. It is
     13 // the caller's responsibility to set it back to true if they wish to
     14 // compress the payload regardless of size.
     15 //
     16 // The TC bit will be set if any records were excluded from the message.
     17 // If the TC bit is already set on the message it will be retained.
     18 // TC indicates that the client should retry over TCP.
     19 //
     20 // According to RFC 2181, the TC bit should only be set if not all of the
     21 // "required" RRs can be included in the response. Unfortunately, we have
     22 // no way of knowing which RRs are required so we set the TC bit if any RR
     23 // had to be omitted from the response.
     24 //
     25 // The appropriate buffer size can be retrieved from the requests OPT
     26 // record, if present, and is transport specific otherwise. dns.MinMsgSize
     27 // should be used for UDP requests without an OPT record, and
     28 // dns.MaxMsgSize for TCP requests without an OPT record.
     29 func (dns *Msg) Truncate(size int) {
     30 	if dns.IsTsig() != nil {
     31 		// To simplify this implementation, we don't perform
     32 		// truncation on responses with a TSIG record.
     33 		return
     34 	}
     35 
     36 	// RFC 6891 mandates that the payload size in an OPT record
     37 	// less than 512 (MinMsgSize) bytes must be treated as equal to 512 bytes.
     38 	//
     39 	// For ease of use, we impose that restriction here.
     40 	if size < MinMsgSize {
     41 		size = MinMsgSize
     42 	}
     43 
     44 	l := msgLenWithCompressionMap(dns, nil) // uncompressed length
     45 	if l <= size {
     46 		// Don't waste effort compressing this message.
     47 		dns.Compress = false
     48 		return
     49 	}
     50 
     51 	dns.Compress = true
     52 
     53 	edns0 := dns.popEdns0()
     54 	if edns0 != nil {
     55 		// Account for the OPT record that gets added at the end,
     56 		// by subtracting that length from our budget.
     57 		//
     58 		// The EDNS(0) OPT record must have the root domain and
     59 		// it's length is thus unaffected by compression.
     60 		size -= Len(edns0)
     61 	}
     62 
     63 	compression := make(map[string]struct{})
     64 
     65 	l = headerSize
     66 	for _, r := range dns.Question {
     67 		l += r.len(l, compression)
     68 	}
     69 
     70 	var numAnswer int
     71 	if l < size {
     72 		l, numAnswer = truncateLoop(dns.Answer, size, l, compression)
     73 	}
     74 
     75 	var numNS int
     76 	if l < size {
     77 		l, numNS = truncateLoop(dns.Ns, size, l, compression)
     78 	}
     79 
     80 	var numExtra int
     81 	if l < size {
     82 		_, numExtra = truncateLoop(dns.Extra, size, l, compression)
     83 	}
     84 
     85 	// See the function documentation for when we set this.
     86 	dns.Truncated = dns.Truncated || len(dns.Answer) > numAnswer ||
     87 		len(dns.Ns) > numNS || len(dns.Extra) > numExtra
     88 
     89 	dns.Answer = dns.Answer[:numAnswer]
     90 	dns.Ns = dns.Ns[:numNS]
     91 	dns.Extra = dns.Extra[:numExtra]
     92 
     93 	if edns0 != nil {
     94 		// Add the OPT record back onto the additional section.
     95 		dns.Extra = append(dns.Extra, edns0)
     96 	}
     97 }
     98 
     99 func truncateLoop(rrs []RR, size, l int, compression map[string]struct{}) (int, int) {
    100 	for i, r := range rrs {
    101 		if r == nil {
    102 			continue
    103 		}
    104 
    105 		l += r.len(l, compression)
    106 		if l > size {
    107 			// Return size, rather than l prior to this record,
    108 			// to prevent any further records being added.
    109 			return size, i
    110 		}
    111 		if l == size {
    112 			return l, i + 1
    113 		}
    114 	}
    115 
    116 	return l, len(rrs)
    117 }