#!/usr/bin/env python3
"""
Generate invalid-content-ranges.pcap with HTTP 206 responses containing
Content-Range headers that are invalid:
  1. last-byte-pos is INT64_MAX, causing signed integer overflow in len = l - f + 1
  2. last-byte-pos is negative (0--5/*), an invalid range

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"
SERVER_PORT = 80

TS_BASE = 1000000000.0


def build_tcp_stream(client_payload, server_payload, client_port, ts_offset):
    """Build a minimal TCP handshake + data exchange + teardown."""
    packets = []
    ts = TS_BASE + ts_offset

    c_seq = 1000
    s_seq = 2000

    cport = client_port

    # SYN
    p = (
        Ether()
        / IP(src=CLIENT_IP, dst=SERVER_IP)
        / TCP(sport=cport, dport=SERVER_PORT, seq=c_seq, ack=0, flags="S")
    )
    p.time = ts
    packets.append(p)
    ts += 0.001
    c_seq += 1

    # SYN-ACK
    p = (
        Ether()
        / IP(src=SERVER_IP, dst=CLIENT_IP)
        / TCP(sport=SERVER_PORT, dport=cport, seq=s_seq, ack=c_seq, flags="SA")
    )
    p.time = ts
    packets.append(p)
    ts += 0.001
    s_seq += 1

    # ACK
    p = (
        Ether()
        / IP(src=CLIENT_IP, dst=SERVER_IP)
        / TCP(sport=cport, dport=SERVER_PORT, seq=c_seq, ack=s_seq, flags="A")
    )
    p.time = ts
    packets.append(p)
    ts += 0.001

    # Client request
    p = (
        Ether()
        / IP(src=CLIENT_IP, dst=SERVER_IP)
        / TCP(sport=cport, dport=SERVER_PORT, seq=c_seq, ack=s_seq, flags="PA")
        / client_payload
    )
    p.time = ts
    packets.append(p)
    ts += 0.001
    c_seq += len(client_payload)

    # Server ACK
    p = (
        Ether()
        / IP(src=SERVER_IP, dst=CLIENT_IP)
        / TCP(sport=SERVER_PORT, dport=cport, seq=s_seq, ack=c_seq, flags="A")
    )
    p.time = ts
    packets.append(p)
    ts += 0.001

    # Server response
    p = (
        Ether()
        / IP(src=SERVER_IP, dst=CLIENT_IP)
        / TCP(sport=SERVER_PORT, dport=cport, seq=s_seq, ack=c_seq, flags="PA")
        / server_payload
    )
    p.time = ts
    packets.append(p)
    ts += 0.001
    s_seq += len(server_payload)

    # Client ACK
    p = (
        Ether()
        / IP(src=CLIENT_IP, dst=SERVER_IP)
        / TCP(sport=cport, dport=SERVER_PORT, seq=c_seq, ack=s_seq, flags="A")
    )
    p.time = ts
    packets.append(p)
    ts += 0.001

    # FIN from server
    p = (
        Ether()
        / IP(src=SERVER_IP, dst=CLIENT_IP)
        / TCP(sport=SERVER_PORT, dport=cport, seq=s_seq, ack=c_seq, flags="FA")
    )
    p.time = ts
    packets.append(p)
    ts += 0.001
    s_seq += 1

    # FIN-ACK from client
    p = (
        Ether()
        / IP(src=CLIENT_IP, dst=SERVER_IP)
        / TCP(sport=cport, dport=SERVER_PORT, seq=c_seq, ack=s_seq, flags="FA")
    )
    p.time = ts
    packets.append(p)
    ts += 0.001
    c_seq += 1

    # Final ACK
    p = (
        Ether()
        / IP(src=SERVER_IP, dst=CLIENT_IP)
        / TCP(sport=SERVER_PORT, dport=cport, seq=s_seq, ack=c_seq, flags="A")
    )
    p.time = ts
    packets.append(p)

    return packets


def make_206(content_range, body=b"A" * 10):
    return (
        b"HTTP/1.1 206 Partial Content\r\n"
        b"Content-Length: 10\r\n"
        b"Content-Range: bytes " + content_range + b"\r\n"
        b"\r\n"
    ) + body


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

    # Connection 1: last-byte-pos is INT64_MAX.
    # Range length (last - first + 1) = INT64_MAX + 1 -> signed integer overflow.
    pkts1 = build_tcp_stream(
        request,
        make_206(b"0-9223372036854775807/9223372036854775808"),
        client_port=44000,
        ts_offset=0.0,
    )

    # Connection 2: negative last-byte-pos (0--5/*).
    # Previously fell through to HTTP_non_positive_content_range;
    # now caught as HTTP_content_range_invalid before the overflow check.
    pkts2 = build_tcp_stream(
        request,
        make_206(b"0--5/*"),
        client_port=44001,
        ts_offset=1.0,
    )

    # Connection 3: last-byte-pos less than first-byte-pos (10-5/*).
    # Caught by the l < f check as HTTP_content_range_invalid.
    pkts3 = build_tcp_stream(
        request,
        make_206(b"10-5/*"),
        client_port=44002,
        ts_offset=2.0,
    )

    all_packets = pkts1 + pkts2 + pkts3
    out = Path(__file__).parent / "invalid-content-ranges.pcap"
    wrpcap(str(out), all_packets)
    print(f"Wrote {len(all_packets)} packets to {out}")


if __name__ == "__main__":
    main()
