#!/usr/bin/env python3
"""
Generate svcb-alpn-malformed-len-too-{long,short}.pcap with DNS HTTPS/SVCB
responses containing malformed ALPN SvcParam encodings.

Generated by Claude Opus 4.6 (Anthropic).
"""

import struct
from pathlib import Path

from scapy.all import DNS, DNSQR, DNSRR, IP, UDP, Ether, wrpcap

SVCB_KEY_ALPN = 1


def build_svcb_rdata(priority, target, svc_params_bytes):
    """Build raw SVCB/HTTPS RDATA from components."""
    target_wire = b""
    if target == ".":
        target_wire = b"\x00"
    else:
        for label in target.rstrip(".").split("."):
            target_wire += bytes([len(label)]) + label.encode()
        target_wire += b"\x00"

    return struct.pack("!H", priority) + target_wire + svc_params_bytes


def build_svc_param(key, value):
    """Build a single SvcParam TLV."""
    return struct.pack("!HH", key, len(value)) + value


def build_alpn_value(protocols):
    """Build an ALPN SvcParam value. Each entry is either a string or a
    (string, length_override) tuple to inject a wrong length byte."""
    value = b""
    for proto in protocols:
        if isinstance(proto, tuple):
            encoded = proto[0].encode()
            value += bytes([proto[1]]) + encoded
        else:
            encoded = proto.encode()
            value += bytes([len(encoded)]) + encoded
    return value


def dns_query(qname):
    return (
        Ether()
        / IP(src="10.0.0.2", dst="10.0.0.1")
        / UDP(sport=1234, dport=53)
        / DNS(
            id=0x1234,
            qr=0,
            qd=DNSQR(qname=qname, qtype=65),
        )
    )


def dns_response(qname, rdata):
    return (
        Ether()
        / IP(src="10.0.0.1", dst="10.0.0.2")
        / UDP(sport=53, dport=1234)
        / DNS(
            id=0x1234,
            qr=1,
            qd=DNSQR(qname=qname, qtype=65),
            an=DNSRR(rrname=qname, type=65, ttl=300, rdata=rdata),
        )
    )


def write_pcap(suffix, packets):
    script_dir = Path(__file__).parent
    out = script_dir / f"svcb-alpn-malformed-{suffix}.pcap"
    wrpcap(str(out), packets)
    print(f"Wrote {len(packets)} packets to {out}")


def main():
    qname = "example.com"

    # len-too-long: ALPN with h3 length overshot (8 instead of 2)
    alpn_value = build_alpn_value(["h2", ("h3", 8)])
    svc_params = build_svc_param(SVCB_KEY_ALPN, alpn_value)
    rdata = build_svcb_rdata(1, ".", svc_params)
    write_pcap("len-too-long", [dns_query(qname), dns_response(qname, rdata)])

    # len-too-short: ALPN with h3 length undershot (1 instead of 2), leaves trailing byte
    alpn_value = build_alpn_value(["h2", ("h3", 1)])
    svc_params = build_svc_param(SVCB_KEY_ALPN, alpn_value)
    rdata = build_svcb_rdata(1, ".", svc_params)
    write_pcap("len-too-short", [dns_query(qname), dns_response(qname, rdata)])


if __name__ == "__main__":
    main()
