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