| | |
| | import time |
| | from scapy.sendrecv import AsyncSniffer |
| | from scapy.all import IP, TCP, UDP, DNS |
| |
|
| | def process_packet(pkt): |
| | """Processes a Scapy packet into a simple dictionary.""" |
| | d = {"timestamp": pkt.time} |
| | if pkt.haslayer(IP): |
| | ip = pkt[IP] |
| | d.update(src=ip.src, dst=ip.dst, ttl=ip.ttl, length=len(pkt)) |
| | if pkt.haslayer(TCP): |
| | t = pkt[TCP] |
| | d.update( |
| | proto=6, src_port=t.sport, dst_port=t.dport, flags=str(t.flags), |
| | info=f"TCP {ip.src}:{t.sport} → {ip.dst}:{t.dport}" |
| | ) |
| | elif pkt.haslayer(UDP): |
| | u = pkt[UDP] |
| | d.update(proto=17, src_port=u.sport, dst_port=u.dport, flags="-") |
| | if pkt.haslayer(DNS) and hasattr(pkt[DNS], 'qd') and pkt[DNS].qd is not None: |
| | qname = pkt[DNS].qd.qname |
| | |
| | qname_str = qname.decode(errors='ignore') if isinstance(qname, bytes) else str(qname) |
| | d.update(proto="DNS", info=f"DNS Query for {qname_str}") |
| | else: |
| | d.update(info=f"UDP {ip.src}:{u.sport} → {ip.dst}:{u.dport}") |
| | else: |
| | d.setdefault("proto", ip.proto) |
| | else: |
| | d.update(src="N/A", dst="N/A", proto="Other", length=len(pkt)) |
| | return d |
| |
|
| | def start_packet_capture(app, socketio, interface=None, filter_expr="ip"): |
| | """ |
| | Sniffs packets in a background thread, adds them to the app's deque, |
| | and emits every packet to connected clients. |
| | """ |
| | def _handle(pkt): |
| | try: |
| | data = process_packet(pkt) |
| | except Exception: |
| | return |
| |
|
| | |
| | app.captured_packets.append(data) |
| |
|
| | |
| | socketio.emit("new_packet", data) |
| |
|
| | sniffer = AsyncSniffer( |
| | iface=interface, |
| | filter=filter_expr, |
| | prn=_handle, |
| | store=False |
| | ) |
| | sniffer.start() |