xref: /aosp_15_r20/external/scapy/scapy/scapypipes.py (revision 7dc08ffc4802948ccbc861daaf1e81c405c2c4bd)
1## This file is part of Scapy
2## See http://www.secdev.org/projects/scapy for more informations
3## Copyright (C) Philippe Biondi <[email protected]>
4## This program is published under a GPLv2 license
5
6from __future__ import print_function
7import socket
8from scapy.modules.six.moves.queue import Queue, Empty
9from scapy.pipetool import Source,Drain,Sink
10from scapy.config import conf
11from scapy.compat import *
12from scapy.utils import PcapReader, PcapWriter
13from scapy.automaton import recv_error
14
15class SniffSource(Source):
16    """Read packets from an interface and send them to low exit.
17     +-----------+
18  >>-|           |->>
19     |           |
20   >-|  [iface]--|->
21     +-----------+
22"""
23    def __init__(self, iface=None, filter=None, name=None):
24        Source.__init__(self, name=name)
25        self.iface = iface
26        self.filter = filter
27    def start(self):
28        self.s = conf.L2listen(iface=self.iface, filter=self.filter)
29    def stop(self):
30        self.s.close()
31    def fileno(self):
32        return self.s.fileno()
33    def check_recv(self):
34        return True
35    def deliver(self):
36        try:
37            self._send(self.s.recv())
38        except recv_error:
39            if not WINDOWS:
40                raise
41
42class RdpcapSource(Source):
43    """Read packets from a PCAP file send them to low exit.
44     +----------+
45  >>-|          |->>
46     |          |
47   >-|  [pcap]--|->
48     +----------+
49"""
50    def __init__(self, fname, name=None):
51        Source.__init__(self, name=name)
52        self.fname = fname
53        self.f = PcapReader(self.fname)
54    def start(self):
55        print("start")
56        self.f = PcapReader(self.fname)
57        self.is_exhausted = False
58    def stop(self):
59        print("stop")
60        self.f.close()
61    def fileno(self):
62        return self.f.fileno()
63    def check_recv(self):
64        return True
65    def deliver(self):
66        p = self.f.recv()
67        print("deliver %r" % p)
68        if p is None:
69            self.is_exhausted = True
70        else:
71            self._send(p)
72
73
74class InjectSink(Sink):
75    """Packets received on low input are injected to an interface
76     +-----------+
77  >>-|           |->>
78     |           |
79   >-|--[iface]  |->
80     +-----------+
81"""
82    def __init__(self, iface=None, name=None):
83        Sink.__init__(self, name=name)
84        if iface == None:
85            iface = conf.iface
86        self.iface = iface
87    def start(self):
88        self.s = conf.L2socket(iface=self.iface)
89    def stop(self):
90        self.s.close()
91    def push(self, msg):
92        self.s.send(msg)
93
94class Inject3Sink(InjectSink):
95    def start(self):
96        self.s = conf.L3socket(iface=self.iface)
97
98
99class WrpcapSink(Sink):
100    """Packets received on low input are written to PCA file
101     +----------+
102  >>-|          |->>
103     |          |
104   >-|--[pcap]  |->
105     +----------+
106"""
107    def __init__(self, fname, name=None):
108        Sink.__init__(self, name=name)
109        self.f = PcapWriter(fname)
110    def stop(self):
111        self.f.flush()
112        self.f.close()
113    def push(self, msg):
114        self.f.write(msg)
115
116
117class UDPDrain(Drain):
118    """UDP payloads received on high entry are sent over UDP
119     +-------------+
120  >>-|--[payload]--|->>
121     |      X      |
122   >-|----[UDP]----|->
123     +-------------+
124"""
125    def __init__(self, ip="127.0.0.1", port=1234):
126        Drain.__init__(self)
127        self.ip = ip
128        self.port = port
129
130    def push(self, msg):
131        from scapy.layers.inet import IP, UDP
132        if IP in msg and msg[IP].proto == 17 and UDP in msg:
133            payload = msg[UDP].payload
134            self._high_send(raw(payload))
135    def high_push(self, msg):
136        from scapy.layers.inet import IP, UDP
137        p = IP(dst=self.ip)/UDP(sport=1234,dport=self.port)/msg
138        self._send(p)
139
140
141class FDSourceSink(Source):
142    """Use a file descriptor as source and sink
143     +-------------+
144  >>-|             |->>
145     |             |
146   >-|-[file desc]-|->
147     +-------------+
148"""
149    def __init__(self, fd, name=None):
150        Source.__init__(self, name=name)
151        self.fd = fd
152    def push(self, msg):
153        self.fd.write(msg)
154    def fileno(self):
155        return self.fd.fileno()
156    def deliver(self):
157        self._send(self.fd.read())
158
159
160class TCPConnectPipe(Source):
161    """TCP connect to addr:port and use it as source and sink
162     +-------------+
163  >>-|             |->>
164     |             |
165   >-|-[addr:port]-|->
166     +-------------+
167"""
168    __selectable_force_select__ = True
169    def __init__(self, addr="", port=0, name=None):
170        Source.__init__(self, name=name)
171        self.addr = addr
172        self.port = port
173        self.fd = None
174    def start(self):
175        self.fd = socket.socket()
176        self.fd.connect((self.addr,self.port))
177    def stop(self):
178        if self.fd:
179            self.fd.close()
180    def push(self, msg):
181        self.fd.send(msg)
182    def fileno(self):
183        return self.fd.fileno()
184    def deliver(self):
185        try:
186            msg = self.fd.recv(65536)
187        except socket.error:
188            self.stop()
189            raise
190        if msg:
191            self._send(msg)
192
193class TCPListenPipe(TCPConnectPipe):
194    """TCP listen on [addr:]port and use first connection as source and sink ; send peer address to high output
195     +------^------+
196  >>-|    +-[peer]-|->>
197     |   /         |
198   >-|-[addr:port]-|->
199     +-------------+
200"""
201    __selectable_force_select__ = True
202    def __init__(self, addr="", port=0, name=None):
203        TCPConnectPipe.__init__(self, addr, port, name)
204        self.connected = False
205        self.q = Queue()
206    def start(self):
207        self.connected = False
208        self.fd = socket.socket()
209        self.fd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
210        self.fd.bind((self.addr,self.port))
211        self.fd.listen(1)
212    def push(self, msg):
213        if self.connected:
214            self.fd.send(msg)
215        else:
216            self.q.put(msg)
217    def deliver(self):
218        if self.connected:
219            try:
220                msg = self.fd.recv(65536)
221            except socket.error:
222                self.stop()
223                raise
224            if msg:
225                self._send(msg)
226        else:
227            fd,frm = self.fd.accept()
228            self._high_send(frm)
229            self.fd.close()
230            self.fd = fd
231            self.connected = True
232            self._trigger(frm)
233            while True:
234                try:
235                    self.fd.send(self.q.get(block=False))
236                except Empty:
237                    break
238
239
240class TriggeredMessage(Drain):
241    """Send a preloaded message when triggered and trigger in chain
242     +------^------+
243  >>-|      | /----|->>
244     |      |/     |
245   >-|-[ message ]-|->
246     +------^------+
247"""
248    def __init__(self, msg, name=None):
249        Drain.__init__(self, name=name)
250        self.msg = msg
251    def on_trigger(self, trigmsg):
252        self._send(self.msg)
253        self._high_send(self.msg)
254        self._trigger(trigmsg)
255
256class TriggerDrain(Drain):
257    """Pass messages and trigger when a condition is met
258     +------^------+
259  >>-|-[condition]-|->>
260     |      |      |
261   >-|-[condition]-|->
262     +-------------+
263"""
264    def __init__(self, f, name=None):
265        Drain.__init__(self, name=name)
266        self.f = f
267    def push(self, msg):
268        v = self.f(msg)
269        if v:
270            self._trigger(v)
271        self._send(msg)
272    def high_push(self, msg):
273        v = self.f(msg)
274        if v:
275            self._trigger(v)
276        self._high_send(msg)
277
278class TriggeredValve(Drain):
279    """Let messages alternatively pass or not, changing on trigger
280     +------^------+
281  >>-|-[pass/stop]-|->>
282     |      |      |
283   >-|-[pass/stop]-|->
284     +------^------+
285"""
286    def __init__(self, start_state=True, name=None):
287        Drain.__init__(self, name=name)
288        self.opened = start_state
289    def push(self, msg):
290        if self.opened:
291            self._send(msg)
292    def high_push(self, msg):
293        if self.opened:
294            self._high_send(msg)
295    def on_trigger(self, msg):
296        self.opened ^= True
297        self._trigger(msg)
298
299class TriggeredQueueingValve(Drain):
300    """Let messages alternatively pass or queued, changing on trigger
301     +------^-------+
302  >>-|-[pass/queue]-|->>
303     |      |       |
304   >-|-[pass/queue]-|->
305     +------^-------+
306"""
307    def __init__(self, start_state=True, name=None):
308        Drain.__init__(self, name=name)
309        self.opened = start_state
310        self.q = Queue()
311    def start(self):
312        self.q = Queue()
313    def push(self, msg):
314        if self.opened:
315            self._send(msg)
316        else:
317            self.q.put((True,msg))
318    def high_push(self, msg):
319        if self.opened:
320            self._send(msg)
321        else:
322            self.q.put((False,msg))
323    def on_trigger(self, msg):
324        self.opened ^= True
325        self._trigger(msg)
326        while True:
327            try:
328                low,msg = self.q.get(block=False)
329            except Empty:
330                break
331            else:
332                if low:
333                    self._send(msg)
334                else:
335                    self._high_send(msg)
336
337class TriggeredSwitch(Drain):
338    """Let messages alternatively high or low, changing on trigger
339     +------^------+
340  >>-|-\    |    /-|->>
341     |  [up/down]  |
342   >-|-/    |    \-|->
343     +------^------+
344"""
345    def __init__(self, start_state=True, name=None):
346        Drain.__init__(self, name=name)
347        self.low = start_state
348    def push(self, msg):
349        if self.low:
350            self._send(msg)
351        else:
352            self._high_send(msg)
353    high_push = push
354    def on_trigger(self, msg):
355        self.low ^= True
356        self._trigger(msg)
357