diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py index e31c83258ea1ca65e07bae0efe6c73721c192508..e242e79336b99c83f38339c49532c37ecb0124b2 100644 --- a/scapy/sendrecv.py +++ b/scapy/sendrecv.py @@ -70,7 +70,6 @@ def _sndrcv_snd(pks, timeout, inter, verbose, tobesent, stopevent): stopevent.wait(timeout) stopevent.set() - class _BreakException(Exception): """A dummy exception used in _get_pkt() to get out of the infinite loop @@ -78,33 +77,15 @@ loop """ pass - -def sndrcv(pks, pkt, timeout=None, inter=0, verbose=None, chainCC=False, - retry=0, multi=False): - 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 +def _sndrcv_rcv(pks, tobesent, stopevent, nbrecv, notans, verbose, chainCC, + multi): + """Function used to recieve packets and check their hashret""" 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) - - hsent={} + hsent = {} for i in tobesent: h = i.hashret() hsent.setdefault(i.hashret(), []).append(i) - if retry < 0: - retry = -retry - autostop = retry - else: - autostop = 0 - if WINDOWS: def _get_pkt(): return pks.recv(MTU) @@ -134,6 +115,69 @@ def sndrcv(pks, pkt, timeout=None, inter=0, verbose=None, chainCC=False, 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): + 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 + # 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 < 0: timeout = None @@ -145,49 +189,8 @@ def sndrcv(pks, pkt, timeout=None, inter=0, verbose=None, chainCC=False, ) thread.start() - 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() - thread.join() - pks.close() + hsent, ans, nbrecv, notans = _sndrcv_rcv(pks, tobesent, stopevent, nbrecv, notans, verbose, chainCC, multi) + thread.join() remain = list(itertools.chain(*six.itervalues(hsent))) if multi: @@ -403,6 +406,8 @@ iface: work only on the given interface""" 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 @@ -467,71 +472,55 @@ def srploop(pkts, *args, **kargs): srloop(pkts, [prn], [inter], [count], ...) --> None""" return __sr_loop(srp, pkts, *args, **kargs) +# SEND/RECV FLOOD METHODS -def sndrcvflood(pks, pkt, prn=lambda s_r:s_r[1].summary(), chainCC=0, store=1, unique=0): +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] received = plist.SndRcvList() seen = {} - hsent={} - for i in tobesent: - h = i.hashret() - if h in hsent: - hsent[h].append(i) - else: - hsent[h] = [i] + stopevent = threading.Event() + count_packets = six.moves.queue.Queue() - def send_in_loop(tobesent): + 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 - packets_to_send = send_in_loop(tobesent) + infinite_gen = send_in_loop(tobesent, stopevent) - ssock = rsock = pks.fileno() + # 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() - try: - while True: - if conf.use_bpf: - from scapy.arch.bpf.supersocket import bpf_select - readyr = bpf_select([rsock]) - _, readys, _ = select([], [ssock], []) - else: - readyr, readys, _ = select([rsock], [ssock], []) + 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 ssock in readys: - pks.send(packets_to_send.next()) - - if rsock in readyr: - p = pks.recv(MTU) - if p is None: - continue - h = p.hashret() - if h in hsent: - hlst = hsent[h] - for i in hlst: - if p.answers(i): - res = prn((i,p)) - if unique: - if res in seen: - continue - seen[res] = None - if res is not None: - print(res) - if store: - received.append((i,p)) - except KeyboardInterrupt: - if chainCC: - raise - return received + 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. Ret val is printed if not None -store: if 1 (default), store answers and return them +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 @@ -541,11 +530,26 @@ iface: listen answers only on the given interface""" 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. Ret val is printed if not None -store: if 1 (default), store answers and return them +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 @@ -557,8 +561,23 @@ iface: listen answers only on the given interface""" 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, diff --git a/scapy/supersocket.py b/scapy/supersocket.py index cd9eb9331479421af8b72673c634337821102dfb..cfc27c559cbc26a823d01627532a5be60494d6a4 100644 --- a/scapy/supersocket.py +++ b/scapy/supersocket.py @@ -192,7 +192,15 @@ class L2ListenTcpdump(SuperSocket): self.outs = None args = ['-w', '-', '-s', '65535'] if iface is not None: - args.extend(['-i', iface]) + if WINDOWS: + try: + args.extend(['-i', iface.pcap_name]) + except AttributeError: + args.extend(['-i', iface]) + else: + args.extend(['-i', iface]) + elif WINDOWS: + args.extend(['-i', conf.iface.pcap_name]) if not promisc: args.append('-p') if not nofilter: @@ -203,9 +211,13 @@ class L2ListenTcpdump(SuperSocket): filter = "not (%s)" % conf.except_filter if filter is not None: args.append(filter) - self.ins = PcapReader(tcpdump(None, prog=prog, args=args, getfd=True)) + self.tcpdump_proc = tcpdump(None, prog=prog, args=args, getproc=True) + self.ins = PcapReader(self.tcpdump_proc.stdout) def recv(self, x=MTU): return self.ins.recv(x) + def close(self): + SuperSocket.close(self) + self.tcpdump_proc.kill() class TunTapInterface(SuperSocket): diff --git a/scapy/utils.py b/scapy/utils.py index 471de79db45a329bf1444c6bcdd6a27dda515dd3..28fa92c3c38a49ec725737b1d5a1b4c4cbba1dc9 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -1200,7 +1200,7 @@ def wireshark(pktlist): @conf.commands.register def tcpdump(pktlist, dump=False, getfd=False, args=None, - prog=None): + prog=None, getproc=False): """Run tcpdump or tshark on a list of packets pktlist: a Packet instance, a PacketList instance or a list of Packet @@ -1211,6 +1211,7 @@ pktlist: a Packet instance, a PacketList instance or a list of Packet dump: when set to True, returns a string instead of displaying it. getfd: when set to True, returns a file-like object to read data from tcpdump or tshark from. +getproc: when set to True, the subprocess.Popen object is returned args: arguments (as a list) to pass to tshark (example for tshark: args=["-T", "json"]). Defaults to ["-n"]. prog: program to use (defaults to tcpdump, will work with tshark) @@ -1249,6 +1250,7 @@ To get a JSON representation of a tshark-parsed PacketList(), one can: u'64' """ + getfd = getfd or getproc if prog is None: prog = [conf.prog.tcpdump] elif isinstance(prog, six.string_types): @@ -1300,6 +1302,8 @@ u'64' proc.stdin.close() if dump: return b"".join(iter(lambda: proc.stdout.read(1048576), b"")) + if getproc: + return proc if getfd: return proc.stdout proc.wait() diff --git a/test/regression.uts b/test/regression.uts index 825e45f0f2d8c4e9ba339c45a192eb2a4ebfc0f6..1e33cb39138d0a3e03370b4fd0d370926b530608 100644 --- a/test/regression.uts +++ b/test/regression.uts @@ -730,6 +730,24 @@ assert x[IP].ottl() in [32, 64, 128, 255] assert 0 <= x[IP].hops() <= 126 x is not None and ICMP in x and x[ICMP].type == 0 += Sending and receiving an ICMP with flooding methods +~ netaccess IP ICMP +# flooding methods do not support timeout. Packing the test for security +def _test_flood(): + old_debug_dissector = conf.debug_dissector + conf.debug_dissector = False + x = sr1flood(IP(dst="www.google.com")/ICMP()) + conf.debug_dissector = old_debug_dissector + x + assert x[IP].ottl() in [32, 64, 128, 255] + assert 0 <= x[IP].hops() <= 126 + x is not None and ICMP in x and x[ICMP].type == 0 + +t = Thread(target=_test_flood) +t.start() +t.join(3) +assert not t.is_alive() + = Sending and receiving an ICMPv6EchoRequest ~ netaccess ipv6 old_debug_dissector = conf.debug_dissector @@ -904,7 +922,7 @@ try: except: from queue import Queue as queue -def _send_or_sniff(pkt, timeout, flt, pid, fork, t_other=None): +def _send_or_sniff(pkt, timeout, flt, pid, fork, t_other=None, opened_socket=None): assert pid != -1 if pid == 0: time.sleep(1) @@ -919,7 +937,7 @@ def _send_or_sniff(pkt, timeout, flt, pid, fork, t_other=None): old_debug_dissector = conf.debug_dissector conf.debug_dissector = False pkts = sniff( - timeout=timeout, filter=flt, + timeout=timeout, filter=flt, opened_socket=opened_socket, stop_filter=lambda p: pkt.__class__ in p and raw(p[pkt.__class__]) == spkt ) conf.debug_dissector = old_debug_dissector @@ -929,18 +947,18 @@ def _send_or_sniff(pkt, timeout, flt, pid, fork, t_other=None): t_other.join() assert raw(pkt) in (raw(p[pkt.__class__]) for p in pkts if pkt.__class__ in p) -def send_and_sniff(pkt, timeout=2, flt=None): +def send_and_sniff(pkt, timeout=2, flt=None, opened_socket=None): """Send a packet, sniff, and check the packet has been seen""" if hasattr(os, "fork"): _send_or_sniff(pkt, timeout, flt, os.fork(), True) else: from threading import Thread - def run_function(pkt, timeout, flt, pid, thread, results): - _send_or_sniff(pkt, timeout, flt, pid, False, t_other=thread) + def run_function(pkt, timeout, flt, pid, thread, results, opened_socket): + _send_or_sniff(pkt, timeout, flt, pid, False, t_other=thread, opened_socket=opened_socket) results.put(True) results = queue() - t_parent = Thread(target=run_function, args=(pkt, timeout, flt, 0, None, results)) - t_child = Thread(target=run_function, args=(pkt, timeout, flt, 1, t_parent, results)) + t_parent = Thread(target=run_function, args=(pkt, timeout, flt, 0, None, results, None)) + t_child = Thread(target=run_function, args=(pkt, timeout, flt, 1, t_parent, results, opened_socket)) t_parent.start() t_child.start() t_parent.join() @@ -953,6 +971,11 @@ send_and_sniff(IP(dst="secdev.org")/ICMP()) send_and_sniff(IP(dst="secdev.org")/ICMP(), flt="icmp") send_and_sniff(Ether()/IP(dst="secdev.org")/ICMP()) +a = L2ListenTcpdump() +icmp_r = IP(b'E\x00\x00\x1c\x00\x01\x00\x00@\x01p\xc0\x7f\x00\x00\x01\xd9\x19\xb2\x05\x08\x00\xf7\xff\x00\x00\x00\x00') +send_and_sniff(icmp_r, timeout=10, opened_socket=a) +a.close() + ############ ############ + ManuFDB tests