<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Arun Sundar</title>
    <description>The latest articles on Forem by Arun Sundar (@arun_sundar_59984c6c07536).</description>
    <link>https://forem.com/arun_sundar_59984c6c07536</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3578364%2F2e4dd4e9-8c5d-422f-9ad2-9b58496ee975.png</url>
      <title>Forem: Arun Sundar</title>
      <link>https://forem.com/arun_sundar_59984c6c07536</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/arun_sundar_59984c6c07536"/>
    <language>en</language>
    <item>
      <title>Introducing packetpy: A Pure-Python Packet Sniffer for Windows 🐍</title>
      <dc:creator>Arun Sundar</dc:creator>
      <pubDate>Wed, 22 Oct 2025 07:32:11 +0000</pubDate>
      <link>https://forem.com/arun_sundar_59984c6c07536/introducing-packetpy-a-pure-python-packet-sniffer-for-windows-b53</link>
      <guid>https://forem.com/arun_sundar_59984c6c07536/introducing-packetpy-a-pure-python-packet-sniffer-for-windows-b53</guid>
      <description>&lt;p&gt;Sniffing network packets in Python has always been tricky — especially on Windows, where raw sockets behave differently than Linux or macOS. I faced this challenge while trying to capture packets on specific ports, parse them, and analyze their payloads programmatically. Existing libraries like PyShark depend on TShark/Wireshark, which adds setup overhead and reduces portability.&lt;/p&gt;

&lt;p&gt;That’s why I created packetpy — a pure-Python, zero-dependency packet sniffer that works natively on Windows and gives fine-grained control over IPv4, TCP, and UDP packets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How packetpy Works?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;packetpy uses raw sockets to capture packets directly from the network interface. Here’s a breakdown of the main components:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Raw Socket Capture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The WinRawSniffer class creates a raw socket:&lt;/p&gt;

&lt;p&gt;s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)&lt;br&gt;
s.bind((iface_ip, 0))&lt;br&gt;
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)&lt;/p&gt;

&lt;p&gt;Key points:&lt;/p&gt;

&lt;p&gt;IP_HDRINCL ensures captured packets include the full IP header.&lt;/p&gt;

&lt;p&gt;RCVALL_ON (via ioctl) enables promiscuous mode to capture all packets.&lt;/p&gt;

&lt;p&gt;The socket runs in a background daemon thread, allowing your script to continue executing while sniffing packets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. IPv4 Parsing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;parse_ipv4(pkt) converts raw bytes into a structured dictionary:&lt;/p&gt;

&lt;p&gt;{'version': ..., 'ihl': ..., 'len': ..., 'proto': ..., 'src': ..., 'dst': ..., 'payload': ...}&lt;/p&gt;

&lt;p&gt;The first 20 bytes of the packet are unpacked using struct.unpack('!BBHHHBBH4s4s', ...).&lt;/p&gt;

&lt;p&gt;The IHL field determines the IP header length.&lt;/p&gt;

&lt;p&gt;Payload extraction is done carefully: pkt[ihl:total_len].&lt;/p&gt;

&lt;p&gt;The result is a dictionary containing source/destination IPs, protocol number, header length, and raw payload.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. TCP and UDP Parsing:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For TCP, parse_tcp(seg) reads:&lt;/p&gt;

&lt;p&gt;src_port, dst_port, seq, ack, offset, payload&lt;/p&gt;

&lt;p&gt;TCP header offset is extracted from the data offset field.&lt;/p&gt;

&lt;p&gt;Payload starts at the calculated offset.&lt;/p&gt;

&lt;p&gt;For UDP, parse_udp(seg) reads:&lt;/p&gt;

&lt;p&gt;src_port, dst_port, length, payload&lt;/p&gt;

&lt;p&gt;UDP headers are always 8 bytes.&lt;/p&gt;

&lt;p&gt;Payload is extracted based on the length field.&lt;/p&gt;

&lt;p&gt;This gives full visibility into the packet structure without any external library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Match Callbacks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;make_match_callback(sniffer, target_ip, target_port) allows you to stop sniffing when a specific packet is detected:&lt;/p&gt;

&lt;p&gt;if ip['dst'] == target_ip and pkt['tcp']['dst_port'] == target_port:&lt;br&gt;
    print("=== MATCH FOUND (TCP) ===")&lt;/p&gt;

&lt;p&gt;Supports TCP and UDP.&lt;/p&gt;

&lt;p&gt;Prints a 512-byte hexdump of the payload for inspection.&lt;/p&gt;

&lt;p&gt;Stops the sniffer automatically once a match is found.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Hexdump Utility&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;hexdump(payload[:512]) formats the payload like this:&lt;/p&gt;

&lt;p&gt;0000  48 65 6c 6c 6f 20 57 6f 72 6c 64              Hello World&lt;br&gt;
0010  ...&lt;/p&gt;

&lt;p&gt;Displays both hex values and ASCII equivalents.&lt;/p&gt;

&lt;p&gt;Useful for quick debugging of raw payload data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Threaded Sniffing Loop&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The sniffer runs in a daemon thread:&lt;/p&gt;

&lt;p&gt;self.thread = threading.Thread(target=loop, daemon=True)&lt;br&gt;
self.thread.start()&lt;/p&gt;

&lt;p&gt;Ensures your main program is not blocked.&lt;/p&gt;

&lt;p&gt;Supports a timeout, after which sniffing stops automatically.&lt;/p&gt;

&lt;p&gt;Exceptions are caught to keep the loop running smoothly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Advantages of packetpy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pure Python, zero dependencies — no TShark or Wireshark required.&lt;/p&gt;

&lt;p&gt;Lightweight and fast — ideal for direct packet inspection on Windows.&lt;/p&gt;

&lt;p&gt;TCP/UDP parsing with payload inspection built-in.&lt;/p&gt;

&lt;p&gt;Callback-based matching — perfect for monitoring specific IPs/ports.&lt;/p&gt;

&lt;p&gt;Threaded design — non-blocking sniffing for your scripts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;pip install packetpy&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start sniffing immediately with:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sniffer = WinRawSniffer(iface_ip="192.168.1.100", timeout=10)&lt;br&gt;
callback = make_match_callback(sniffer, target_ip="192.168.1.105", target_port=8080)&lt;br&gt;
sniffer.start(callback)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next Steps&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’m planning to add:&lt;/p&gt;

&lt;p&gt;TLS packet detection&lt;/p&gt;

&lt;p&gt;Cross-platform support (Linux/macOS)&lt;/p&gt;

&lt;p&gt;Packet blocking simulations&lt;/p&gt;

&lt;p&gt;packetpy taught me a lot about raw sockets, network byte order, and protocol structures. Python gives you amazing low-level control when you dig deep.&lt;/p&gt;

&lt;p&gt;💡 Try it out and share feedback!&lt;br&gt;
Network enthusiasts, Python devs, and security researchers — what features should come next?&lt;/p&gt;

</description>
      <category>networking</category>
      <category>python</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
