From 9cb3e418e15f74393292bdc49c3c70a805bbb284 Mon Sep 17 00:00:00 2001
From: Pierre LALET <pierre.lalet@cea.fr>
Date: Tue, 22 Aug 2017 20:07:51 +0200
Subject: [PATCH] Merge sndrcv() for Unix & Windows systems

---
 scapy/arch/windows/compatibility.py | 146 +---------------------------
 scapy/sendrecv.py                   |  97 ++++++++++--------
 2 files changed, 57 insertions(+), 186 deletions(-)

diff --git a/scapy/arch/windows/compatibility.py b/scapy/arch/windows/compatibility.py
index 0b4700c0..b60a86ad 100644
--- a/scapy/arch/windows/compatibility.py
+++ b/scapy/arch/windows/compatibility.py
@@ -29,150 +29,6 @@ import scapy.modules.six as six
 
 WINDOWS = True
 
-def sndrcv(pks, pkt, timeout = 2, inter = 0, verbose=None, chainCC=0, retry=0, multi=0):
-    if not isinstance(pkt, Gen):
-        pkt = SetGen(pkt)
-        
-    if verbose is None:
-        verbose = conf.verb
-    from scapy.sendrecv import debug
-    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
-    all_stimuli = tobesent = [p for p in pkt]
-    notans = len(tobesent)
-
-    hsent={}
-    for i in tobesent:
-        h = i.hashret()
-        if h in hsent:
-            hsent[h].append(i)
-        else:
-            hsent[h] = [i]
-    if retry < 0:
-        retry = -retry
-        autostop=retry
-    else:
-        autostop=0
-
-
-    while retry >= 0:
-        found=0
-    
-        if timeout < 0:
-            timeout = None
-
-        pid=1
-        try:
-            if WINDOWS or pid == 0:
-                try:
-                    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.exception("--- Error sending packets")
-                        log_runtime.info("--- Error sending packets")
-                finally:
-                    try:
-                        sent_times = [p.sent_time for p in all_stimuli if p.sent_time]
-                    except:
-                        pass
-            if WINDOWS or pid > 0:
-                # Timeout starts after last packet is sent (as in Unix version) 
-                if timeout:
-                    stoptime = time.time()+timeout
-                else:
-                    stoptime = 0
-                remaintime = None
-                try:
-                    try:
-                        while True:
-                            if stoptime:
-                                remaintime = stoptime-time.time()
-                                if remaintime <= 0:
-                                    break
-                            r = pks.recv(MTU)
-                            if r is None:
-                                continue
-                            ok = 0
-                            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 = 1
-                                        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
-                finally:
-                    if WINDOWS:
-                        for p,t in zip(all_stimuli, sent_times):
-                            p.sent_time = t
-        finally:
-            pass
-
-        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 s,r in ans:
-            if hasattr(s, '_answered'):
-                del(s._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")
-
-
-import scapy.sendrecv as sendrecv
-sendrecv.sndrcv = sndrcv
 
 def sniff(count=0, store=1, offline=None, prn = None, stop_filter=None, lfilter=None, L2socket=None, timeout=None, *arg, **karg):
     """Sniff packets
@@ -241,7 +97,7 @@ stop_filter: python function applied to each packet to determine
     return plist.PacketList(lst,"Sniffed")
 
 import scapy.sendrecv
-sendrecv.sniff = sniff
+scapy.sendrecv.sniff = sniff
 
 # If wpcap.dll is not available
 if not (conf.use_winpcapy or conf.use_pcap or conf.use_dnet):
diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py
index b404f88f..806431ad 100644
--- a/scapy/sendrecv.py
+++ b/scapy/sendrecv.py
@@ -14,7 +14,7 @@ import os, sys, time, subprocess
 import itertools
 from select import select, error as select_error
 
-from scapy.consts import DARWIN, FREEBSD, OPENBSD
+from scapy.consts import DARWIN, FREEBSD, OPENBSD, WINDOWS
 from scapy.data import *
 from scapy.config import conf
 from scapy.packet import Gen
@@ -64,34 +64,34 @@ def sndrcv(pks, pkt, timeout = None, inter = 0, verbose=None, chainCC=0, retry=0
     hsent={}
     for i in tobesent:
         h = i.hashret()
-        if h in hsent:
-            hsent[h].append(i)
-        else:
-            hsent[h] = [i]
+        hsent.setdefault(i.hashret(), []).append(i)
+
     if retry < 0:
         retry = -retry
-        autostop=retry
+        autostop = retry
     else:
-        autostop=0
-
+        autostop = 0
 
     while retry >= 0:
-        found=0
-    
+        found = 0
+
         if timeout < 0:
             timeout = None
-            
-        rdpipe,wrpipe = os.pipe()
-        rdpipe=os.fdopen(rdpipe)
-        wrpipe=os.fdopen(wrpipe,"w")
 
-        pid=1
+        if not WINDOWS:
+            rdpipe, wrpipe = os.pipe()
+            rdpipe = os.fdopen(rdpipe)
+            wrpipe = os.fdopen(wrpipe,"w")
+
+        pid = None
         try:
-            pid = os.fork()
-            if pid == 0:
+            if not WINDOWS:
+                pid = os.fork()
+            if WINDOWS or pid == 0:
                 try:
-                    sys.stdin.close()
-                    rdpipe.close()
+                    if not WINDOWS:
+                        sys.stdin.close()
+                        rdpipe.close()
                     try:
                         i = 0
                         if verbose:
@@ -107,23 +107,33 @@ def sndrcv(pks, pkt, timeout = None, inter = 0, verbose=None, chainCC=0, retry=0
                     except KeyboardInterrupt:
                         pass
                     except:
-                        log_runtime.exception("--- Error in child %i" % os.getpid())
-                        log_runtime.info("--- Error in child %i" % os.getpid())
+                        if WINDOWS:
+                            log_runtime.exception("--- Error sending packets")
+                            log_runtime.info("--- Error sending packets")
+                        else:
+                            log_runtime.exception("--- Error in child "
+                                                  "%i" % os.getpid())
+                            log_runtime.info("--- Error in child "
+                                             "%i" % os.getpid())
                 finally:
                     try:
-                        os.setpgrp() # Chance process group to avoid ctrl-C
                         sent_times = [p.sent_time for p in all_stimuli if p.sent_time]
-                        six.moves.cPickle.dump( (conf.netcache,sent_times), wrpipe )
-                        wrpipe.close()
+                        if not WINDOWS:
+                            # Change process group to avoid ctrl-C
+                            os.setpgrp()
+                            six.moves.cPickle.dump((conf.netcache, sent_times),
+                                                   wrpipe)
+                            wrpipe.close()
                     except:
                         pass
-            elif pid < 0:
+            elif not WINDOWS and pid < 0:
                 log_runtime.error("fork error")
-            else:
-                wrpipe.close()
+            elif WINDOWS or pid > 0:
+                if not WINDOWS:
+                    wrpipe.close()
+                    inmask = [rdpipe, pks]
                 stoptime = 0
                 remaintime = None
-                inmask = [rdpipe,pks]
                 try:
                     try:
                         while True:
@@ -132,19 +142,21 @@ def sndrcv(pks, pkt, timeout = None, inter = 0, verbose=None, chainCC=0, retry=0
                                 if remaintime <= 0:
                                     break
                             r = None
-                            if conf.use_bpf:
+                            if WINDOWS:
+                                r = pks.recv(MTU)
+                            elif conf.use_bpf:
                                 from scapy.arch.bpf.supersocket import bpf_select
                                 inp = bpf_select(inmask)
                                 if pks in inp:
                                     r = pks.recv()
                             elif not isinstance(pks, StreamSocket) and (FREEBSD or DARWIN or OPENBSD):
-                                inp, out, err = select(inmask,[],[], 0.05)
+                                inp, _, _ = select(inmask, [], [], 0.05)
                                 if len(inp) == 0 or pks in inp:
                                     r = pks.nonblock_recv()
                             else:
                                 inp = []
                                 try:
-                                    inp, out, err = select(inmask,[],[], remaintime)
+                                    inp, _, _ = select(inmask, [], [], remaintime)
                                 except (IOError, select_error) as exc:
                                     # select.error has no .errno attribute
                                     if exc.args[0] != errno.EINTR:
@@ -153,9 +165,9 @@ def sndrcv(pks, pkt, timeout = None, inter = 0, verbose=None, chainCC=0, retry=0
                                     break
                                 if pks in inp:
                                     r = pks.recv(MTU)
-                            if rdpipe in inp:
+                            if WINDOWS and rdpipe in inp:
                                 if timeout:
-                                    stoptime = time.time()+timeout
+                                    stoptime = time.time() + timeout
                                 del(inmask[inmask.index(rdpipe)])
                             if r is None:
                                 continue
@@ -189,13 +201,16 @@ def sndrcv(pks, pkt, timeout = None, inter = 0, verbose=None, chainCC=0, retry=0
                         if chainCC:
                             raise
                 finally:
-                    try:
-                        nc,sent_times = six.moves.cPickle.load(rdpipe)
-                    except EOFError:
-                        warning("Child died unexpectedly. Packets may have not been sent %i"%os.getpid())
+                    if not WINDOWS:
+                        try:
+                            nc, sent_times = six.moves.cPickle.load(rdpipe)
+                        except EOFError:
+                            warning("Child died unexpectedly. "
+                                    "Packets may have not been sent.")
                     else:
-                        conf.netcache.update(nc)
-                        for p,t in zip(all_stimuli, sent_times):
+                        if not WINDOWS:
+                            conf.netcache.update(nc)
+                        for p, t in zip(all_stimuli, sent_times):
                             p.sent_time = t
                     os.waitpid(pid,0)
         finally:
@@ -213,7 +228,7 @@ def sndrcv(pks, pkt, timeout = None, inter = 0, verbose=None, chainCC=0, retry=0
         if len(tobesent) == 0:
             break
         retry -= 1
-        
+
     if conf.debug_match:
         debug.sent=plist.PacketList(remain[:],"Sent")
         debug.match=plist.SndRcvList(ans[:])
@@ -223,7 +238,7 @@ def sndrcv(pks, pkt, timeout = None, inter = 0, verbose=None, chainCC=0, retry=0
         for s,r in ans:
             if hasattr(s, '_answered'):
                 del(s._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")
-- 
GitLab