| ## This file is part of Scapy |
| ## See http://www.secdev.org/projects/scapy for more informations |
| ## Copyright (C) Philippe Biondi <phil@secdev.org> |
| ## This program is published under a GPLv2 license |
| |
| from __future__ import print_function |
| import socket |
| from scapy.modules.six.moves.queue import Queue, Empty |
| from scapy.pipetool import Source,Drain,Sink |
| from scapy.config import conf |
| from scapy.compat import * |
| from scapy.utils import PcapReader, PcapWriter |
| from scapy.automaton import recv_error |
| |
| class SniffSource(Source): |
| """Read packets from an interface and send them to low exit. |
| +-----------+ |
| >>-| |->> |
| | | |
| >-| [iface]--|-> |
| +-----------+ |
| """ |
| def __init__(self, iface=None, filter=None, name=None): |
| Source.__init__(self, name=name) |
| self.iface = iface |
| self.filter = filter |
| def start(self): |
| self.s = conf.L2listen(iface=self.iface, filter=self.filter) |
| def stop(self): |
| self.s.close() |
| def fileno(self): |
| return self.s.fileno() |
| def check_recv(self): |
| return True |
| def deliver(self): |
| try: |
| self._send(self.s.recv()) |
| except recv_error: |
| if not WINDOWS: |
| raise |
| |
| class RdpcapSource(Source): |
| """Read packets from a PCAP file send them to low exit. |
| +----------+ |
| >>-| |->> |
| | | |
| >-| [pcap]--|-> |
| +----------+ |
| """ |
| def __init__(self, fname, name=None): |
| Source.__init__(self, name=name) |
| self.fname = fname |
| self.f = PcapReader(self.fname) |
| def start(self): |
| print("start") |
| self.f = PcapReader(self.fname) |
| self.is_exhausted = False |
| def stop(self): |
| print("stop") |
| self.f.close() |
| def fileno(self): |
| return self.f.fileno() |
| def check_recv(self): |
| return True |
| def deliver(self): |
| p = self.f.recv() |
| print("deliver %r" % p) |
| if p is None: |
| self.is_exhausted = True |
| else: |
| self._send(p) |
| |
| |
| class InjectSink(Sink): |
| """Packets received on low input are injected to an interface |
| +-----------+ |
| >>-| |->> |
| | | |
| >-|--[iface] |-> |
| +-----------+ |
| """ |
| def __init__(self, iface=None, name=None): |
| Sink.__init__(self, name=name) |
| if iface == None: |
| iface = conf.iface |
| self.iface = iface |
| def start(self): |
| self.s = conf.L2socket(iface=self.iface) |
| def stop(self): |
| self.s.close() |
| def push(self, msg): |
| self.s.send(msg) |
| |
| class Inject3Sink(InjectSink): |
| def start(self): |
| self.s = conf.L3socket(iface=self.iface) |
| |
| |
| class WrpcapSink(Sink): |
| """Packets received on low input are written to PCA file |
| +----------+ |
| >>-| |->> |
| | | |
| >-|--[pcap] |-> |
| +----------+ |
| """ |
| def __init__(self, fname, name=None): |
| Sink.__init__(self, name=name) |
| self.f = PcapWriter(fname) |
| def stop(self): |
| self.f.flush() |
| self.f.close() |
| def push(self, msg): |
| self.f.write(msg) |
| |
| |
| class UDPDrain(Drain): |
| """UDP payloads received on high entry are sent over UDP |
| +-------------+ |
| >>-|--[payload]--|->> |
| | X | |
| >-|----[UDP]----|-> |
| +-------------+ |
| """ |
| def __init__(self, ip="127.0.0.1", port=1234): |
| Drain.__init__(self) |
| self.ip = ip |
| self.port = port |
| |
| def push(self, msg): |
| from scapy.layers.inet import IP, UDP |
| if IP in msg and msg[IP].proto == 17 and UDP in msg: |
| payload = msg[UDP].payload |
| self._high_send(raw(payload)) |
| def high_push(self, msg): |
| from scapy.layers.inet import IP, UDP |
| p = IP(dst=self.ip)/UDP(sport=1234,dport=self.port)/msg |
| self._send(p) |
| |
| |
| class FDSourceSink(Source): |
| """Use a file descriptor as source and sink |
| +-------------+ |
| >>-| |->> |
| | | |
| >-|-[file desc]-|-> |
| +-------------+ |
| """ |
| def __init__(self, fd, name=None): |
| Source.__init__(self, name=name) |
| self.fd = fd |
| def push(self, msg): |
| self.fd.write(msg) |
| def fileno(self): |
| return self.fd.fileno() |
| def deliver(self): |
| self._send(self.fd.read()) |
| |
| |
| class TCPConnectPipe(Source): |
| """TCP connect to addr:port and use it as source and sink |
| +-------------+ |
| >>-| |->> |
| | | |
| >-|-[addr:port]-|-> |
| +-------------+ |
| """ |
| __selectable_force_select__ = True |
| def __init__(self, addr="", port=0, name=None): |
| Source.__init__(self, name=name) |
| self.addr = addr |
| self.port = port |
| self.fd = None |
| def start(self): |
| self.fd = socket.socket() |
| self.fd.connect((self.addr,self.port)) |
| def stop(self): |
| if self.fd: |
| self.fd.close() |
| def push(self, msg): |
| self.fd.send(msg) |
| def fileno(self): |
| return self.fd.fileno() |
| def deliver(self): |
| try: |
| msg = self.fd.recv(65536) |
| except socket.error: |
| self.stop() |
| raise |
| if msg: |
| self._send(msg) |
| |
| class TCPListenPipe(TCPConnectPipe): |
| """TCP listen on [addr:]port and use first connection as source and sink ; send peer address to high output |
| +------^------+ |
| >>-| +-[peer]-|->> |
| | / | |
| >-|-[addr:port]-|-> |
| +-------------+ |
| """ |
| __selectable_force_select__ = True |
| def __init__(self, addr="", port=0, name=None): |
| TCPConnectPipe.__init__(self, addr, port, name) |
| self.connected = False |
| self.q = Queue() |
| def start(self): |
| self.connected = False |
| self.fd = socket.socket() |
| self.fd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
| self.fd.bind((self.addr,self.port)) |
| self.fd.listen(1) |
| def push(self, msg): |
| if self.connected: |
| self.fd.send(msg) |
| else: |
| self.q.put(msg) |
| def deliver(self): |
| if self.connected: |
| try: |
| msg = self.fd.recv(65536) |
| except socket.error: |
| self.stop() |
| raise |
| if msg: |
| self._send(msg) |
| else: |
| fd,frm = self.fd.accept() |
| self._high_send(frm) |
| self.fd.close() |
| self.fd = fd |
| self.connected = True |
| self._trigger(frm) |
| while True: |
| try: |
| self.fd.send(self.q.get(block=False)) |
| except Empty: |
| break |
| |
| |
| class TriggeredMessage(Drain): |
| """Send a preloaded message when triggered and trigger in chain |
| +------^------+ |
| >>-| | /----|->> |
| | |/ | |
| >-|-[ message ]-|-> |
| +------^------+ |
| """ |
| def __init__(self, msg, name=None): |
| Drain.__init__(self, name=name) |
| self.msg = msg |
| def on_trigger(self, trigmsg): |
| self._send(self.msg) |
| self._high_send(self.msg) |
| self._trigger(trigmsg) |
| |
| class TriggerDrain(Drain): |
| """Pass messages and trigger when a condition is met |
| +------^------+ |
| >>-|-[condition]-|->> |
| | | | |
| >-|-[condition]-|-> |
| +-------------+ |
| """ |
| def __init__(self, f, name=None): |
| Drain.__init__(self, name=name) |
| self.f = f |
| def push(self, msg): |
| v = self.f(msg) |
| if v: |
| self._trigger(v) |
| self._send(msg) |
| def high_push(self, msg): |
| v = self.f(msg) |
| if v: |
| self._trigger(v) |
| self._high_send(msg) |
| |
| class TriggeredValve(Drain): |
| """Let messages alternatively pass or not, changing on trigger |
| +------^------+ |
| >>-|-[pass/stop]-|->> |
| | | | |
| >-|-[pass/stop]-|-> |
| +------^------+ |
| """ |
| def __init__(self, start_state=True, name=None): |
| Drain.__init__(self, name=name) |
| self.opened = start_state |
| def push(self, msg): |
| if self.opened: |
| self._send(msg) |
| def high_push(self, msg): |
| if self.opened: |
| self._high_send(msg) |
| def on_trigger(self, msg): |
| self.opened ^= True |
| self._trigger(msg) |
| |
| class TriggeredQueueingValve(Drain): |
| """Let messages alternatively pass or queued, changing on trigger |
| +------^-------+ |
| >>-|-[pass/queue]-|->> |
| | | | |
| >-|-[pass/queue]-|-> |
| +------^-------+ |
| """ |
| def __init__(self, start_state=True, name=None): |
| Drain.__init__(self, name=name) |
| self.opened = start_state |
| self.q = Queue() |
| def start(self): |
| self.q = Queue() |
| def push(self, msg): |
| if self.opened: |
| self._send(msg) |
| else: |
| self.q.put((True,msg)) |
| def high_push(self, msg): |
| if self.opened: |
| self._send(msg) |
| else: |
| self.q.put((False,msg)) |
| def on_trigger(self, msg): |
| self.opened ^= True |
| self._trigger(msg) |
| while True: |
| try: |
| low,msg = self.q.get(block=False) |
| except Empty: |
| break |
| else: |
| if low: |
| self._send(msg) |
| else: |
| self._high_send(msg) |
| |
| class TriggeredSwitch(Drain): |
| """Let messages alternatively high or low, changing on trigger |
| +------^------+ |
| >>-|-\ | /-|->> |
| | [up/down] | |
| >-|-/ | \-|-> |
| +------^------+ |
| """ |
| def __init__(self, start_state=True, name=None): |
| Drain.__init__(self, name=name) |
| self.low = start_state |
| def push(self, msg): |
| if self.low: |
| self._send(msg) |
| else: |
| self._high_send(msg) |
| high_push = push |
| def on_trigger(self, msg): |
| self.low ^= True |
| self._trigger(msg) |