blob: 07b84c59659d162082063d950c0f0af9af9f97e7 [file] [log] [blame]
## 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)