#!/usr/bin/env python3
"""
Generate content-length-overflow.pcap: HTTP response with a Content-Length
value larger than INT_MAX (2147483647). The body is truncated to a few bytes
so the PCAP stays small.

The bug: ContentLine_Analyzer::DoDeliver() used std::min<int>(plain_delivery_length, len)
where plain_delivery_length is int64_t. When plain_delivery_length > INT_MAX the
narrowing cast to int produces a negative number, corrupting both the data pointer
and the remaining length passed to DoDeliverOnce(), causing a crash.

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

from pathlib import Path

from scapy.all import IP, TCP, Ether, wrpcap

CLIENT_IP = "10.0.0.1"
SERVER_IP = "10.0.0.2"
CLIENT_PORT = 54321
SERVER_PORT = 80

# 2.2 GB — well above INT_MAX (2147483647), below UINT_MAX
CONTENT_LENGTH = 2200000000


def pkt(src_ip, dst_ip, sport, dport, flags, seq, ack, payload=b""):
    return (
        Ether(src="00:00:00:00:00:01", dst="00:00:00:00:00:02")
        / IP(src=src_ip, dst=dst_ip)
        / TCP(sport=sport, dport=dport, flags=flags, seq=seq, ack=ack)
        / payload
    )


def cpkt(flags, seq, ack, payload=b""):
    return pkt(CLIENT_IP, SERVER_IP, CLIENT_PORT, SERVER_PORT, flags, seq, ack, payload)


def spkt(flags, seq, ack, payload=b""):
    return pkt(SERVER_IP, CLIENT_IP, SERVER_PORT, CLIENT_PORT, flags, seq, ack, payload)


def main():
    request = b"GET /bigfile HTTP/1.1\r\nHost: 10.0.0.2\r\n\r\n"

    response_header = (
        b"HTTP/1.1 200 OK\r\n"
        b"Content-Length: %d\r\n"
        b"Content-Type: application/octet-stream\r\n"
        b"\r\n"
    ) % CONTENT_LENGTH

    # A small body fragment — Zeek must not crash processing this.
    body_fragment = b"A" * 100

    response = response_header + body_fragment

    c_seq = 1000
    s_seq = 2000

    pkts = []

    # Three-way handshake
    pkts.append(cpkt("S", c_seq, 0))
    c_seq += 1
    pkts.append(spkt("SA", s_seq, c_seq))
    s_seq += 1
    pkts.append(cpkt("A", c_seq, s_seq))

    # Client request
    pkts.append(cpkt("PA", c_seq, s_seq, request))
    c_seq += len(request)
    pkts.append(spkt("A", s_seq, c_seq))

    # Server response: header + body fragment in one packet
    pkts.append(spkt("PA", s_seq, c_seq, response))
    s_seq += len(response)
    pkts.append(cpkt("A", c_seq, s_seq))

    # Teardown
    pkts.append(spkt("FA", s_seq, c_seq))
    s_seq += 1
    pkts.append(cpkt("FA", c_seq, s_seq))
    c_seq += 1
    pkts.append(spkt("A", s_seq, c_seq))

    out = Path(__file__).with_suffix("")
    wrpcap(str(out), pkts)
    print(f"Wrote {len(pkts)} packets to {out}")


if __name__ == "__main__":
    main()
