| ## 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 |
| |
| """ |
| Functions to send and receive packets. |
| """ |
| |
| from __future__ import absolute_import, print_function |
| import errno |
| import itertools |
| import threading |
| import os |
| from select import select, error as select_error |
| import subprocess |
| import time |
| |
| from scapy.consts import DARWIN, FREEBSD, OPENBSD, WINDOWS |
| from scapy.data import ETH_P_ALL, MTU |
| from scapy.config import conf |
| from scapy.packet import Gen |
| from scapy.utils import get_temp_file, PcapReader, tcpdump, wrpcap |
| from scapy import plist |
| from scapy.error import log_runtime, log_interactive |
| from scapy.base_classes import SetGen |
| from scapy.supersocket import StreamSocket, L3RawSocket, L2ListenTcpdump |
| from scapy.modules import six |
| from scapy.modules.six.moves import map |
| if conf.route is None: |
| # unused import, only to initialize conf.route |
| import scapy.route |
| from scapy.supersocket import SuperSocket |
| |
| ################# |
| ## Debug class ## |
| ################# |
| |
| class debug: |
| recv=[] |
| sent=[] |
| match=[] |
| |
| |
| #################### |
| ## Send / Receive ## |
| #################### |
| |
| |
| def _sndrcv_snd(pks, timeout, inter, verbose, tobesent, stopevent): |
| """Function used in the sending thread of sndrcv()""" |
| try: |
| i = 0 |
| if verbose: |
| print("Begin emission:") |
| for p in tobesent: |
| pks.send(p) |
| i += 1 |
| time.sleep(inter) |
| if verbose: |
| print("Finished to send %i packets." % i) |
| except SystemExit: |
| pass |
| except KeyboardInterrupt: |
| pass |
| except: |
| log_runtime.info("--- Error sending packets", exc_info=True) |
| if timeout is not None: |
| stopevent.wait(timeout) |
| stopevent.set() |
| |
| class _BreakException(Exception): |
| """A dummy exception used in _get_pkt() to get out of the infinite |
| loop |
| |
| """ |
| pass |
| |
| def _sndrcv_rcv(pks, tobesent, stopevent, nbrecv, notans, verbose, chainCC, |
| multi): |
| """Function used to recieve packets and check their hashret""" |
| ans = [] |
| hsent = {} |
| for i in tobesent: |
| h = i.hashret() |
| hsent.setdefault(i.hashret(), []).append(i) |
| |
| if WINDOWS: |
| def _get_pkt(): |
| return pks.recv(MTU) |
| elif conf.use_bpf: |
| from scapy.arch.bpf.supersocket import bpf_select |
| def _get_pkt(): |
| if bpf_select([pks]): |
| return pks.recv() |
| elif (conf.use_pcap and not isinstance(pks, (StreamSocket, L3RawSocket, L2ListenTcpdump))) or \ |
| (not isinstance(pks, (StreamSocket, L2ListenTcpdump)) and (DARWIN or FREEBSD or OPENBSD)): |
| def _get_pkt(): |
| res = pks.nonblock_recv() |
| if res is None: |
| time.sleep(0.05) |
| return res |
| else: |
| def _get_pkt(): |
| try: |
| inp, _, _ = select([pks], [], [], 0.05) |
| except (IOError, select_error) as exc: |
| # select.error has no .errno attribute |
| if exc.args[0] != errno.EINTR: |
| raise |
| else: |
| if inp: |
| return pks.recv(MTU) |
| if stopevent.is_set(): |
| raise _BreakException() |
| |
| try: |
| try: |
| while True: |
| r = _get_pkt() |
| if r is None: |
| if stopevent.is_set(): |
| break |
| continue |
| ok = False |
| h = r.hashret() |
| if h in hsent: |
| hlst = hsent[h] |
| for i, sentpkt in enumerate(hlst): |
| if r.answers(sentpkt): |
| ans.append((sentpkt, r)) |
| if verbose > 1: |
| os.write(1, b"*") |
| ok = True |
| if not multi: |
| del hlst[i] |
| notans -= 1 |
| else: |
| if not hasattr(sentpkt, '_answered'): |
| notans -= 1 |
| sentpkt._answered = 1 |
| break |
| if notans == 0 and not multi: |
| break |
| if not ok: |
| if verbose > 1: |
| os.write(1, b".") |
| nbrecv += 1 |
| if conf.debug_match: |
| debug.recv.append(r) |
| except KeyboardInterrupt: |
| if chainCC: |
| raise |
| except _BreakException: |
| pass |
| finally: |
| stopevent.set() |
| return (hsent, ans, nbrecv, notans) |
| |
| def sndrcv(pks, pkt, timeout=None, inter=0, verbose=None, chainCC=False, |
| retry=0, multi=False, rcv_pks=None): |
| """Scapy raw function to send a packet and recieve its answer. |
| WARNING: This is an internal function. Using sr/srp/sr1/srp is |
| more appropriate in many cases. |
| |
| pks: SuperSocket instance to send/recieve packets |
| pkt: the packet to send |
| rcv_pks: if set, will be used instead of pks to recieve packets. packets will still |
| be sent through pks |
| nofilter: put 1 to avoid use of BPF filters |
| retry: if positive, how many times to resend unanswered packets |
| if negative, how many times to retry when no more packets are answered |
| timeout: how much time to wait after the last packet has been sent |
| verbose: set verbosity level |
| multi: whether to accept multiple answers for the same stimulus""" |
| if not isinstance(pkt, Gen): |
| pkt = SetGen(pkt) |
| if verbose is None: |
| verbose = conf.verb |
| debug.recv = plist.PacketList([],"Unanswered") |
| debug.sent = plist.PacketList([],"Sent") |
| debug.match = plist.SndRcvList([]) |
| nbrecv = 0 |
| ans = [] |
| # do it here to fix random fields, so that parent and child have the same |
| tobesent = [p for p in pkt] |
| notans = len(tobesent) |
| |
| if retry < 0: |
| retry = -retry |
| autostop = retry |
| else: |
| autostop = 0 |
| |
| while retry >= 0: |
| if timeout is not None and timeout < 0: |
| timeout = None |
| stopevent = threading.Event() |
| |
| thread = threading.Thread( |
| target=_sndrcv_snd, |
| args=(pks, timeout, inter, verbose, tobesent, stopevent), |
| ) |
| thread.start() |
| |
| hsent, newans, nbrecv, notans = _sndrcv_rcv( |
| (rcv_pks or pks), tobesent, stopevent, nbrecv, notans, verbose, chainCC, multi, |
| ) |
| thread.join() |
| ans.extend(newans) |
| |
| remain = list(itertools.chain(*six.itervalues(hsent))) |
| if multi: |
| remain = [p for p in remain if not hasattr(p, '_answered')] |
| |
| if autostop and len(remain) > 0 and len(remain) != len(tobesent): |
| retry = autostop |
| |
| tobesent = remain |
| if len(tobesent) == 0: |
| break |
| retry -= 1 |
| |
| if conf.debug_match: |
| debug.sent=plist.PacketList(remain[:], "Sent") |
| debug.match=plist.SndRcvList(ans[:]) |
| |
| # Clean the ans list to delete the field _answered |
| if multi: |
| for snd, _ in ans: |
| if hasattr(snd, '_answered'): |
| del snd._answered |
| |
| if verbose: |
| print("\nReceived %i packets, got %i answers, remaining %i packets" % (nbrecv+len(ans), len(ans), notans)) |
| return plist.SndRcvList(ans), plist.PacketList(remain, "Unanswered") |
| |
| |
| def __gen_send(s, x, inter=0, loop=0, count=None, verbose=None, realtime=None, return_packets=False, *args, **kargs): |
| if isinstance(x, str): |
| x = conf.raw_layer(load=x) |
| if not isinstance(x, Gen): |
| x = SetGen(x) |
| if verbose is None: |
| verbose = conf.verb |
| n = 0 |
| if count is not None: |
| loop = -count |
| elif not loop: |
| loop = -1 |
| if return_packets: |
| sent_packets = plist.PacketList() |
| try: |
| while loop: |
| dt0 = None |
| for p in x: |
| if realtime: |
| ct = time.time() |
| if dt0: |
| st = dt0+p.time-ct |
| if st > 0: |
| time.sleep(st) |
| else: |
| dt0 = ct-p.time |
| s.send(p) |
| if return_packets: |
| sent_packets.append(p) |
| n += 1 |
| if verbose: |
| os.write(1,b".") |
| time.sleep(inter) |
| if loop < 0: |
| loop += 1 |
| except KeyboardInterrupt: |
| pass |
| s.close() |
| if verbose: |
| print("\nSent %i packets." % n) |
| if return_packets: |
| return sent_packets |
| |
| @conf.commands.register |
| def send(x, inter=0, loop=0, count=None, verbose=None, realtime=None, return_packets=False, socket=None, |
| *args, **kargs): |
| """Send packets at layer 3 |
| send(packets, [inter=0], [loop=0], [count=None], [verbose=conf.verb], [realtime=None], [return_packets=False], |
| [socket=None]) -> None""" |
| if socket is None: |
| socket = conf.L3socket(*args, **kargs) |
| return __gen_send(socket, x, inter=inter, loop=loop, count=count,verbose=verbose, |
| realtime=realtime, return_packets=return_packets) |
| |
| @conf.commands.register |
| def sendp(x, inter=0, loop=0, iface=None, iface_hint=None, count=None, verbose=None, realtime=None, |
| return_packets=False, socket=None, *args, **kargs): |
| """Send packets at layer 2 |
| sendp(packets, [inter=0], [loop=0], [iface=None], [iface_hint=None], [count=None], [verbose=conf.verb], |
| [realtime=None], [return_packets=False], [socket=None]) -> None""" |
| if iface is None and iface_hint is not None and socket is None: |
| iface = conf.route.route(iface_hint)[0] |
| if socket is None: |
| socket = conf.L2socket(iface=iface, *args, **kargs) |
| return __gen_send(socket, x, inter=inter, loop=loop, count=count, |
| verbose=verbose, realtime=realtime, return_packets=return_packets) |
| |
| @conf.commands.register |
| def sendpfast(x, pps=None, mbps=None, realtime=None, loop=0, file_cache=False, iface=None): |
| """Send packets at layer 2 using tcpreplay for performance |
| pps: packets per second |
| mpbs: MBits per second |
| realtime: use packet's timestamp, bending time with real-time value |
| loop: number of times to process the packet list |
| file_cache: cache packets in RAM instead of reading from disk at each iteration |
| iface: output interface """ |
| if iface is None: |
| iface = conf.iface |
| argv = [conf.prog.tcpreplay, "--intf1=%s" % iface ] |
| if pps is not None: |
| argv.append("--pps=%i" % pps) |
| elif mbps is not None: |
| argv.append("--mbps=%f" % mbps) |
| elif realtime is not None: |
| argv.append("--multiplier=%f" % realtime) |
| else: |
| argv.append("--topspeed") |
| |
| if loop: |
| argv.append("--loop=%i" % loop) |
| if file_cache: |
| argv.append("--preload-pcap") |
| |
| f = get_temp_file() |
| argv.append(f) |
| wrpcap(f, x) |
| try: |
| subprocess.check_call(argv) |
| except KeyboardInterrupt: |
| log_interactive.info("Interrupted by user") |
| except Exception: |
| if conf.interactive: |
| log_interactive.error("Cannot execute [%s]", argv[0], exc_info=True) |
| else: |
| raise |
| finally: |
| os.unlink(f) |
| |
| |
| |
| |
| |
| @conf.commands.register |
| def sr(x, promisc=None, filter=None, iface=None, nofilter=0, *args,**kargs): |
| """Send and receive packets at layer 3 |
| nofilter: put 1 to avoid use of BPF filters |
| retry: if positive, how many times to resend unanswered packets |
| if negative, how many times to retry when no more packets are answered |
| timeout: how much time to wait after the last packet has been sent |
| verbose: set verbosity level |
| multi: whether to accept multiple answers for the same stimulus |
| filter: provide a BPF filter |
| iface: listen answers only on the given interface""" |
| if "timeout" not in kargs: |
| kargs["timeout"] = -1 |
| s = conf.L3socket(promisc=promisc, filter=filter, iface=iface, nofilter=nofilter) |
| result = sndrcv(s, x, *args, **kargs) |
| s.close() |
| return result |
| |
| @conf.commands.register |
| def sr1(x, promisc=None, filter=None, iface=None, nofilter=0, *args,**kargs): |
| """Send packets at layer 3 and return only the first answer |
| nofilter: put 1 to avoid use of BPF filters |
| retry: if positive, how many times to resend unanswered packets |
| if negative, how many times to retry when no more packets are answered |
| timeout: how much time to wait after the last packet has been sent |
| verbose: set verbosity level |
| multi: whether to accept multiple answers for the same stimulus |
| filter: provide a BPF filter |
| iface: listen answers only on the given interface""" |
| if "timeout" not in kargs: |
| kargs["timeout"] = -1 |
| s=conf.L3socket(promisc=promisc, filter=filter, nofilter=nofilter, iface=iface) |
| ans, _ = sndrcv(s, x, *args, **kargs) |
| s.close() |
| if len(ans) > 0: |
| return ans[0][1] |
| else: |
| return None |
| |
| @conf.commands.register |
| def srp(x, promisc=None, iface=None, iface_hint=None, filter=None, nofilter=0, type=ETH_P_ALL, *args,**kargs): |
| """Send and receive packets at layer 2 |
| nofilter: put 1 to avoid use of BPF filters |
| retry: if positive, how many times to resend unanswered packets |
| if negative, how many times to retry when no more packets are answered |
| timeout: how much time to wait after the last packet has been sent |
| verbose: set verbosity level |
| multi: whether to accept multiple answers for the same stimulus |
| filter: provide a BPF filter |
| iface: work only on the given interface""" |
| if "timeout" not in kargs: |
| kargs["timeout"] = -1 |
| if iface is None and iface_hint is not None: |
| iface = conf.route.route(iface_hint)[0] |
| s = conf.L2socket(promisc=promisc, iface=iface, filter=filter, nofilter=nofilter, type=type) |
| result = sndrcv(s, x, *args, **kargs) |
| s.close() |
| return result |
| |
| @conf.commands.register |
| def srp1(*args,**kargs): |
| """Send and receive packets at layer 2 and return only the first answer |
| nofilter: put 1 to avoid use of BPF filters |
| retry: if positive, how many times to resend unanswered packets |
| if negative, how many times to retry when no more packets are answered |
| timeout: how much time to wait after the last packet has been sent |
| verbose: set verbosity level |
| multi: whether to accept multiple answers for the same stimulus |
| filter: provide a BPF filter |
| iface: work only on the given interface""" |
| if "timeout" not in kargs: |
| kargs["timeout"] = -1 |
| ans, _ = srp(*args, **kargs) |
| if len(ans) > 0: |
| return ans[0][1] |
| else: |
| return None |
| |
| # SEND/RECV LOOP METHODS |
| |
| def __sr_loop(srfunc, pkts, prn=lambda x:x[1].summary(), prnfail=lambda x:x.summary(), inter=1, timeout=None, count=None, verbose=None, store=1, *args, **kargs): |
| n = 0 |
| r = 0 |
| ct = conf.color_theme |
| if verbose is None: |
| verbose = conf.verb |
| parity = 0 |
| ans=[] |
| unans=[] |
| if timeout is None: |
| timeout = min(2*inter, 5) |
| try: |
| while True: |
| parity ^= 1 |
| col = [ct.even,ct.odd][parity] |
| if count is not None: |
| if count == 0: |
| break |
| count -= 1 |
| start = time.time() |
| if verbose > 1: |
| print("\rsend...\r", end=' ') |
| res = srfunc(pkts, timeout=timeout, verbose=0, chainCC=True, *args, **kargs) |
| n += len(res[0])+len(res[1]) |
| r += len(res[0]) |
| if verbose > 1 and prn and len(res[0]) > 0: |
| msg = "RECV %i:" % len(res[0]) |
| print("\r"+ct.success(msg), end=' ') |
| for p in res[0]: |
| print(col(prn(p))) |
| print(" "*len(msg), end=' ') |
| if verbose > 1 and prnfail and len(res[1]) > 0: |
| msg = "fail %i:" % len(res[1]) |
| print("\r"+ct.fail(msg), end=' ') |
| for p in res[1]: |
| print(col(prnfail(p))) |
| print(" "*len(msg), end=' ') |
| if verbose > 1 and not (prn or prnfail): |
| print("recv:%i fail:%i" % tuple(map(len, res[:2]))) |
| if store: |
| ans += res[0] |
| unans += res[1] |
| end=time.time() |
| if end-start < inter: |
| time.sleep(inter+start-end) |
| except KeyboardInterrupt: |
| pass |
| |
| if verbose and n>0: |
| print(ct.normal("\nSent %i packets, received %i packets. %3.1f%% hits." % (n,r,100.0*r/n))) |
| return plist.SndRcvList(ans),plist.PacketList(unans) |
| |
| @conf.commands.register |
| def srloop(pkts, *args, **kargs): |
| """Send a packet at layer 3 in loop and print the answer each time |
| srloop(pkts, [prn], [inter], [count], ...) --> None""" |
| return __sr_loop(sr, pkts, *args, **kargs) |
| |
| @conf.commands.register |
| def srploop(pkts, *args, **kargs): |
| """Send a packet at layer 2 in loop and print the answer each time |
| srloop(pkts, [prn], [inter], [count], ...) --> None""" |
| return __sr_loop(srp, pkts, *args, **kargs) |
| |
| # SEND/RECV FLOOD METHODS |
| |
| def sndrcvflood(pks, pkt, inter=0, verbose=None, chainCC=False, prn=lambda x: x): |
| if not verbose: |
| verbose = conf.verb |
| if not isinstance(pkt, Gen): |
| pkt = SetGen(pkt) |
| tobesent = [p for p in pkt] |
| |
| stopevent = threading.Event() |
| count_packets = six.moves.queue.Queue() |
| |
| def send_in_loop(tobesent, stopevent, count_packets=count_packets): |
| """Infinite generator that produces the same packet until stopevent is triggered.""" |
| while True: |
| for p in tobesent: |
| if stopevent.is_set(): |
| raise StopIteration() |
| count_packets.put(0) |
| yield p |
| |
| infinite_gen = send_in_loop(tobesent, stopevent) |
| |
| # We don't use _sndrcv_snd verbose (it messes the logs up as in a thread that ends after recieving) |
| thread = threading.Thread( |
| target=_sndrcv_snd, |
| args=(pks, None, inter, False, infinite_gen, stopevent), |
| ) |
| thread.start() |
| |
| hsent, ans, nbrecv, notans = _sndrcv_rcv(pks, tobesent, stopevent, 0, len(tobesent), verbose, chainCC, False) |
| thread.join() |
| remain = list(itertools.chain(*six.itervalues(hsent))) |
| # Apply prn |
| ans = [(x, prn(y)) for (x, y) in ans] |
| |
| if verbose: |
| print("\nReceived %i packets, got %i answers, remaining %i packets. Sent a total of %i packets." % (nbrecv+len(ans), len(ans), notans, count_packets.qsize())) |
| count_packets.empty() |
| del count_packets |
| |
| return plist.SndRcvList(ans), plist.PacketList(remain, "Unanswered") |
| |
| @conf.commands.register |
| def srflood(x, promisc=None, filter=None, iface=None, nofilter=None, *args,**kargs): |
| """Flood and receive packets at layer 3 |
| prn: function applied to packets received |
| unique: only consider packets whose print |
| nofilter: put 1 to avoid use of BPF filters |
| filter: provide a BPF filter |
| iface: listen answers only on the given interface""" |
| s = conf.L3socket(promisc=promisc, filter=filter, iface=iface, nofilter=nofilter) |
| r=sndrcvflood(s,x,*args,**kargs) |
| s.close() |
| return r |
| |
| @conf.commands.register |
| def sr1flood(x, promisc=None, filter=None, iface=None, nofilter=0, *args,**kargs): |
| """Flood and receive packets at layer 3 and return only the first answer |
| prn: function applied to packets received |
| verbose: set verbosity level |
| nofilter: put 1 to avoid use of BPF filters |
| filter: provide a BPF filter |
| iface: listen answers only on the given interface""" |
| s=conf.L3socket(promisc=promisc, filter=filter, nofilter=nofilter, iface=iface) |
| ans, _ = sndrcvflood(s, x, *args, **kargs) |
| s.close() |
| if len(ans) > 0: |
| return ans[0][1] |
| else: |
| return None |
| |
| @conf.commands.register |
| def srpflood(x, promisc=None, filter=None, iface=None, iface_hint=None, nofilter=None, *args,**kargs): |
| """Flood and receive packets at layer 2 |
| prn: function applied to packets received |
| unique: only consider packets whose print |
| nofilter: put 1 to avoid use of BPF filters |
| filter: provide a BPF filter |
| iface: listen answers only on the given interface""" |
| if iface is None and iface_hint is not None: |
| iface = conf.route.route(iface_hint)[0] |
| s = conf.L2socket(promisc=promisc, filter=filter, iface=iface, nofilter=nofilter) |
| r=sndrcvflood(s,x,*args,**kargs) |
| s.close() |
| return r |
| |
| @conf.commands.register |
| def srp1flood(x, promisc=None, filter=None, iface=None, nofilter=0, *args,**kargs): |
| """Flood and receive packets at layer 2 and return only the first answer |
| prn: function applied to packets received |
| verbose: set verbosity level |
| nofilter: put 1 to avoid use of BPF filters |
| filter: provide a BPF filter |
| iface: listen answers only on the given interface""" |
| s=conf.L2socket(promisc=promisc, filter=filter, nofilter=nofilter, iface=iface) |
| ans, _ = sndrcvflood(s, x, *args, **kargs) |
| s.close() |
| if len(ans) > 0: |
| return ans[0][1] |
| else: |
| return None |
| |
| # SNIFF METHODS |
| |
| @conf.commands.register |
| def sniff(count=0, store=True, offline=None, prn=None, lfilter=None, |
| L2socket=None, timeout=None, opened_socket=None, |
| stop_filter=None, iface=None, *arg, **karg): |
| """ |
| |
| Sniff packets and return a list of packets. |
| |
| Arguments: |
| |
| count: number of packets to capture. 0 means infinity. |
| |
| store: whether to store sniffed packets or discard them |
| |
| prn: function to apply to each packet. If something is returned, it |
| is displayed. |
| |
| Ex: prn = lambda x: x.summary() |
| |
| filter: BPF filter to apply. |
| |
| lfilter: Python function applied to each packet to determine if |
| further action may be done. |
| |
| Ex: lfilter = lambda x: x.haslayer(Padding) |
| |
| offline: PCAP file (or list of PCAP files) to read packets from, |
| instead of sniffing them |
| |
| timeout: stop sniffing after a given time (default: None). |
| |
| L2socket: use the provided L2socket (default: use conf.L2listen). |
| |
| opened_socket: provide an object (or a list of objects) ready to use |
| .recv() on. |
| |
| stop_filter: Python function applied to each packet to determine if |
| we have to stop the capture after this packet. |
| |
| Ex: stop_filter = lambda x: x.haslayer(TCP) |
| |
| iface: interface or list of interfaces (default: None for sniffing |
| on all interfaces). |
| |
| The iface, offline and opened_socket parameters can be either an |
| element, a list of elements, or a dict object mapping an element to a |
| label (see examples below). |
| |
| Examples: |
| |
| >>> sniff(filter="arp") |
| |
| >>> sniff(lfilter=lambda pkt: ARP in pkt) |
| |
| >>> sniff(iface="eth0", prn=Packet.summary) |
| |
| >>> sniff(iface=["eth0", "mon0"], |
| ... prn=lambda pkt: "%s: %s" % (pkt.sniffed_on, |
| ... pkt.summary())) |
| |
| >>> sniff(iface={"eth0": "Ethernet", "mon0": "Wifi"}, |
| ... prn=lambda pkt: "%s: %s" % (pkt.sniffed_on, |
| ... pkt.summary())) |
| |
| """ |
| c = 0 |
| sniff_sockets = {} # socket: label dict |
| if opened_socket is not None: |
| if isinstance(opened_socket, list): |
| sniff_sockets.update((s, "socket%d" % i) |
| for i, s in enumerate(opened_socket)) |
| elif isinstance(opened_socket, dict): |
| sniff_sockets.update((s, label) |
| for s, label in six.iteritems(opened_socket)) |
| else: |
| sniff_sockets[opened_socket] = "socket0" |
| if offline is not None: |
| flt = karg.get('filter') |
| if isinstance(offline, list): |
| sniff_sockets.update((PcapReader( |
| fname if flt is None else |
| tcpdump(fname, args=["-w", "-", flt], getfd=True) |
| ), fname) for fname in offline) |
| elif isinstance(offline, dict): |
| sniff_sockets.update((PcapReader( |
| fname if flt is None else |
| tcpdump(fname, args=["-w", "-", flt], getfd=True) |
| ), label) for fname, label in six.iteritems(offline)) |
| else: |
| sniff_sockets[PcapReader( |
| offline if flt is None else |
| tcpdump(offline, args=["-w", "-", flt], getfd=True) |
| )] = offline |
| if not sniff_sockets or iface is not None: |
| if L2socket is None: |
| L2socket = conf.L2listen |
| if isinstance(iface, list): |
| sniff_sockets.update( |
| (L2socket(type=ETH_P_ALL, iface=ifname, *arg, **karg), ifname) |
| for ifname in iface |
| ) |
| elif isinstance(iface, dict): |
| sniff_sockets.update( |
| (L2socket(type=ETH_P_ALL, iface=ifname, *arg, **karg), iflabel) |
| for ifname, iflabel in six.iteritems(iface) |
| ) |
| else: |
| sniff_sockets[L2socket(type=ETH_P_ALL, iface=iface, |
| *arg, **karg)] = iface |
| lst = [] |
| if timeout is not None: |
| stoptime = time.time()+timeout |
| remain = None |
| read_allowed_exceptions = () |
| if conf.use_bpf: |
| from scapy.arch.bpf.supersocket import bpf_select |
| def _select(sockets): |
| return bpf_select(sockets, remain) |
| elif WINDOWS: |
| from scapy.arch.pcapdnet import PcapTimeoutElapsed |
| read_allowed_exceptions = (PcapTimeoutElapsed,) |
| def _select(sockets): |
| try: |
| return sockets |
| except PcapTimeoutElapsed: |
| return [] |
| else: |
| def _select(sockets): |
| try: |
| return select(sockets, [], [], remain)[0] |
| except select_error as exc: |
| # Catch 'Interrupted system call' errors |
| if exc[0] == errno.EINTR: |
| return [] |
| raise |
| try: |
| while sniff_sockets: |
| if timeout is not None: |
| remain = stoptime-time.time() |
| if remain <= 0: |
| break |
| ins = _select(sniff_sockets) |
| for s in ins: |
| try: |
| p = s.recv() |
| except read_allowed_exceptions: |
| continue |
| if p is None: |
| del sniff_sockets[s] |
| break |
| if lfilter and not lfilter(p): |
| continue |
| p.sniffed_on = sniff_sockets[s] |
| if store: |
| lst.append(p) |
| c += 1 |
| if prn: |
| r = prn(p) |
| if r is not None: |
| print(r) |
| if stop_filter and stop_filter(p): |
| sniff_sockets = [] |
| break |
| if 0 < count <= c: |
| sniff_sockets = [] |
| break |
| except KeyboardInterrupt: |
| pass |
| if opened_socket is None: |
| for s in sniff_sockets: |
| s.close() |
| return plist.PacketList(lst,"Sniffed") |
| |
| |
| @conf.commands.register |
| def bridge_and_sniff(if1, if2, xfrm12=None, xfrm21=None, prn=None, L2socket=None, |
| *args, **kargs): |
| """Forward traffic between interfaces if1 and if2, sniff and return |
| the exchanged packets. |
| |
| Arguments: |
| |
| if1, if2: the interfaces to use (interface names or opened sockets). |
| |
| xfrm12: a function to call when forwarding a packet from if1 to |
| if2. If it returns True, the packet is forwarded as it. If it |
| returns False or None, the packet is discarded. If it returns a |
| packet, this packet is forwarded instead of the original packet |
| one. |
| |
| xfrm21: same as xfrm12 for packets forwarded from if2 to if1. |
| |
| The other arguments are the same than for the function sniff(), |
| except for offline, opened_socket and iface that are ignored. |
| See help(sniff) for more. |
| |
| """ |
| for arg in ['opened_socket', 'offline', 'iface']: |
| if arg in kargs: |
| log_runtime.warning("Argument %s cannot be used in " |
| "bridge_and_sniff() -- ignoring it.", arg) |
| del kargs[arg] |
| def _init_socket(iface, count): |
| if isinstance(iface, SuperSocket): |
| return iface, "iface%d" % count |
| else: |
| return (L2socket or conf.L2socket)(iface=iface), iface |
| sckt1, if1 = _init_socket(if1, 1) |
| sckt2, if2 = _init_socket(if2, 2) |
| peers = {if1: sckt2, if2: sckt1} |
| xfrms = {} |
| if xfrm12 is not None: |
| xfrms[if1] = xfrm12 |
| if xfrm21 is not None: |
| xfrms[if2] = xfrm21 |
| def prn_send(pkt): |
| try: |
| sendsock = peers[pkt.sniffed_on] |
| except KeyError: |
| return |
| if pkt.sniffed_on in xfrms: |
| try: |
| newpkt = xfrms[pkt.sniffed_on](pkt) |
| except: |
| log_runtime.warning( |
| 'Exception in transformation function for packet [%s] ' |
| 'received on %s -- dropping', |
| pkt.summary(), pkt.sniffed_on, exc_info=True |
| ) |
| return |
| else: |
| if newpkt is True: |
| newpkt = pkt.original |
| elif not newpkt: |
| return |
| else: |
| newpkt = pkt.original |
| try: |
| sendsock.send(newpkt) |
| except: |
| log_runtime.warning('Cannot forward packet [%s] received on %s', |
| pkt.summary(), pkt.sniffed_on, exc_info=True) |
| if prn is None: |
| prn = prn_send |
| else: |
| prn_orig = prn |
| def prn(pkt): |
| prn_send(pkt) |
| return prn_orig(pkt) |
| |
| return sniff(opened_socket={sckt1: if1, sckt2: if2}, prn=prn, |
| *args, **kargs) |
| |
| |
| @conf.commands.register |
| def tshark(*args,**kargs): |
| """Sniff packets and print them calling pkt.summary(), a bit like text wireshark""" |
| print("Capturing on '" + str(kargs.get('iface') if 'iface' in kargs else conf.iface) + "'") |
| i = [0] # This should be a nonlocal variable, using a mutable object for Python 2 compatibility |
| def _cb(pkt): |
| print("%5d\t%s" % (i[0], pkt.summary())) |
| i[0] += 1 |
| sniff(prn=_cb, store=False, *args, **kargs) |
| print("\n%d packet%s captured" % (i[0], 's' if i[0] > 1 else '')) |