# Buggy code
def qname_encode(name: str) -> bytes:
    """Encode QNAME without using name compression."""
    encoded = bytearray()
    labels = name.split(".")
    # Ensure there's an empty label for the root domain
    if labels[-1] != "":
        labels.append("")
    for label in labels:
        encoded_label = label.encode("idna")
        encoded_length = len(encoded_label)
        # Length of the encoded label, in bytes, but a maximum of 63
        # The maximum is 63 as the upper two bits are used as a flag for name
        # compression.
        encoded.append(min(encoded_length, 63))
        if encoded_length == 0:
            # If we've reached an empty label, assume this is the last component.
            # Empty labels (two periods right after each other) isn't legal anyways.
            break
        if encoded_length > 63:
            _LOGGER.warning(
                (
                    "A label (%s) isn being truncated (to %s) in the DNS name '%s' "
                    "as it is over 63 bytes long."
                ),
                label,
                label[:64],
                name,
            )
            encoded.extend(encoded_label[:64])
        else:
            encoded.extend(encoded_label)
    return encoded


# Traceback (UnicodeDecodeError)
$  atvremote --debug --mdns-debug scan
...
2021-01-09 22:15:43 ERROR: bad stuff
Traceback (most recent call last):
File "/home/postlund/pyatv_dev/pyatv/pyatv/support/mdns.py", line 337, in datagram_received
services = parse_services(DnsMessage().unpack(data))
File "/home/postlund/pyatv_dev/pyatv/pyatv/support/dns.py", line 291, in unpack
self.answers.extend(
File "/home/postlund/pyatv_dev/pyatv/pyatv/support/dns.py", line 292, in <genexpr>
DnsResource.unpack_read(buffer) for _ in range((header.ancount))
File "/home/postlund/pyatv_dev/pyatv/pyatv/support/dns.py", line 258, in unpack_read
rd = qtype.parse_rdata(buffer, rd_length)
File "/home/postlund/pyatv_dev/pyatv/pyatv/support/dns.py", line 173, in parse_rdata
return parse_domain_name(buffer)
File "/home/postlund/pyatv_dev/pyatv/pyatv/support/dns.py", line 108, in parse_domain_name
return ".".join(label.decode("idna") for label in labels)
File "/home/postlund/pyatv_dev/pyatv/pyatv/support/dns.py", line 108, in <genexpr>
return ".".join(label.decode("idna") for label in labels)
File "/usr/local/lib/python3.8/encodings/idna.py", line 214, in decode
result.append(ToUnicode(label))
File "/usr/local/lib/python3.8/encodings/idna.py", line 125, in ToUnicode
return str(label, "ascii")
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 5: ordinal not in range(128)
2021-01-09 22:15:43 DEBUG: Failed to decode message (Msg=35ff840000030003000000000b5f6170706c6574762d7632045f746370056c6f63616c00000c80010b5f746f7563682d61626c65c018000c8001085f616972706c6179c018000c8001c028000c00010000000a00131032444632453735303736333943323639c028c00c000c00010000000a00131032444632453735303736333943323639c00cc03a000c00010000000a0013104170706c65c2a0545620283431363729c03a)
...

# Fixed code
def qname_encode(name: typing.Union[str, typing.Sequence[str]]) -> bytes:
    """Encode QNAME without using name compression.

    Labels (each component of a domain name) are encoded using UTF-8, as that is what
    the Apple TV has been observed to use for all domain names.

    This function can take either a single string, with each label separated by dots, or
    a sequence of strings, and each element of the sequence is treated as a single
    label. A null (empty) label is added for the root domain if it is not already
    present for both types of arguments.
    """
    encoded = bytearray()
    labels: typing.List[str]
    if isinstance(name, collections.abc.Sequence) and not isinstance(name, str):
        # Copy the sequence so we can make changes to it
        labels = list(name) or []
    else:
        # Try to parse it as a service instance name, so we can handle the instance
        # label having dots in it.
        try:
            srv_name = ServiceInstanceName.split_name(name)
        except ValueError:
            labels = typing.cast(str, name).split(".")
        else:
            labels = []
            if srv_name.instance:
                labels.append(srv_name.instance)
            # the ptr_name just has the instance name dropped off
            labels.extend(srv_name.ptr_name.split("."))
    # Ensure there's always an empty label for the root domain
    if not labels or labels[-1] != "":
        labels.append("")
    # DNS-SD uses UTF-8 for names, not IDNA. Apple extends this to basically all places
    # where names are used (except for A/AAAA records, which are transliterated!).
    encoding = "utf-8"
    # Normalize all labels using NFC, as specified in RFC 6763, section 4.1.3
    normalized_labels = (unicodedata.normalize("NFC", label) for label in labels)
    for label in normalized_labels:
        encoded_label = label.encode(encoding)
        encoded_length = len(encoded_label)
        # When truncating the label, we can't just stop at 63 bytes as that might be
        # splitting a multi-byte Unicode codepoint.
        truncated = False
        while encoded_length > 63:
            truncated = True
            truncated_label = encoded_label.decode(encoding)[:-1]
            encoded_label = truncated_label.encode(encoding)
            encoded_length = len(encoded_label)
        if truncated:
            _LOGGER.warning(
                (
                    "A label (%s) is being truncated (to %s) in the DNS name '%s' "
                    "as it is over 63 bytes long."
                ),
                label,
                encoded_label.decode(encoding),
                name,
            )
        encoded.append(encoded_length)
        if encoded_length == 0:
            # If we've reached an empty label, assume this is the last component.
            # Empty labels (two periods right after each other) aren't legal anyways.
            break
        else:
            encoded.extend(encoded_label)
    return encoded