docs: def dns_compress(pkt):
      """This function compresses a DNS packet according to compression rules.
      """
      if DNS not in pkt:
          raise Scapy_Exception("Can only compress DNS layers")
      pkt = pkt.copy()
      dns_pkt = pkt.getlayer(DNS)
      build_pkt = raw(dns_pkt)
  
      def field_gen(dns_pkt):
          """Iterates through all DNS strings that can be compressed"""
          for lay in [dns_pkt.qd, dns_pkt.an, dns_pkt.ns, dns_pkt.ar]:
              if lay is None:
                  continue
              current = lay
              while not isinstance(current, NoPayload):
                  if isinstance(current, InheritOriginDNSStrPacket):
                      for field in current.fields_desc:
                          if isinstance(field, DNSStrField) or \
                             (isinstance(field, MultipleTypeField) and
                             current.type in [2, 5, 12]):
                              # Get the associated data and store it accordingly  # noqa: E501
                              dat = current.getfieldval(field.name)
                              yield current, field.name, dat
                  current = current.payload
  
      def possible_shortens(dat):
          """Iterates through all possible compression parts in a DNS string"""
          yield dat
          for x in range(1, dat.count(b".")):
              yield dat.split(b".", x)[x]
      data = {}
      burned_data = 0
      for current, name, dat in field_gen(dns_pkt):
          for part in possible_shortens(dat):
              # Encode the data
              encoded = dns_encode(part, check_built=True)
              if part not in data:
                  # We have no occurrence of such data, let's store it as a
                  # possible pointer for future strings.
                  # We get the index of the encoded data
                  index = build_pkt.index(encoded)
                  index -= burned_data
                  # The following is used to build correctly the pointer
                  fb_index = ((index >> 8) | 0xc0)
                  sb_index = index - (256 * (fb_index - 0xc0))
                  pointer = chb(fb_index) + chb(sb_index)
                  data[part] = [(current, name, pointer)]
              else:
                  # This string already exists, let's mark the current field
                  # with it, so that it gets compressed
                  data[part].append((current, name))
                  # calculate spared space
                  burned_data += len(encoded) - 2
                  break
      # Apply compression rules
      for ck in data:
          # compression_key is a DNS string
          replacements = data[ck]
          # replacements is the list of all tuples (layer, field name)
          # where this string was found
          replace_pointer = replacements.pop(0)[2]
          # replace_pointer is the packed pointer that should replace
          # those strings. Note that pop remove it from the list
          for rep in replacements:
              # setfieldval edits the value of the field in the layer
              val = rep[0].getfieldval(rep[1])
              assert val.endswith(ck)
              kept_string = dns_encode(val[:-len(ck)], check_built=True)[:-1]
              new_val = kept_string + replace_pointer
              rep[0].setfieldval(rep[1], new_val)
              try:
                  del(rep[0].rdlen)
              except AttributeError:
                  pass
      # End of the compression algorithm
      # Destroy the previous DNS layer if needed
      if not isinstance(pkt, DNS) and pkt.getlayer(DNS).underlayer:
          pkt.getlayer(DNS).underlayer.remove_payload()
          return pkt / dns_pkt
      return dns_pkt