Repository: secdev/scapy

Issue:
Crash when multi is set to 1
Here is a new regression from v2.4.0 on Linux.

Current `master`:
```
>>> conf.checkIPsrc = False ; sr(IPv6(dst="ff02::1")/ICMPv6EchoRequest(), multi=1, timeout=1)
Begin emission:
Finished sending 1 packets.
*---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-2-e46442714862> in <module>()
----> 1 conf.checkIPsrc = False ; sr(IPv6(dst="ff02::1")/ICMPv6EchoRequest(), multi=1, timeout=1)

/home/guedou/Projects/Scapy/scapy-issues/scapy/sendrecv.py in sr(x, promisc, filter, iface, nofilter, *args, **kargs)
    461     s = conf.L3socket(promisc=promisc, filter=filter,
    462                       iface=iface, nofilter=nofilter)
--> 463     result = sndrcv(s, x, *args, **kargs)
    464     s.close()
    465     return result

/home/guedou/Projects/Scapy/scapy-issues/scapy/sendrecv.py in sndrcv(*args, **kwargs)
    259     more appropriate in many cases.
    260     """
--> 261     sndrcver = SndRcvHandler(*args, **kwargs)
    262     return sndrcver.results()
    263 

/home/guedou/Projects/Scapy/scapy-issues/scapy/sendrecv.py in __init__(self, pks, pkt, timeout, inter, verbose, chainCC, retry, multi, rcv_pks, prebuild, _flood, session)
    134 
    135             # Start routine with callback
--> 136             self._sndrcv_rcv(snd_thread.start)
    137 
    138             # Ended. Let's close gracefully

/home/guedou/Projects/Scapy/scapy-issues/scapy/sendrecv.py in _sndrcv_rcv(self, callback)
    247                 opened_socket=self.pks,
    248                 session=self.session,
--> 249                 started_callback=callback
    250             )
    251         except KeyboardInterrupt:

/home/guedou/Projects/Scapy/scapy-issues/scapy/sendrecv.py in _run(self, count, store, offline, prn, lfilter, L2socket, timeout, opened_socket, stop_filter, iface, started_callback, session, session_args, session_kwargs, *arg, **karg)
    923                     p.sniffed_on = sniff_sockets[s]
    924                     # on_packet_received handles the prn/storage
--> 925                     session.on_packet_received(p)
    926                     # check
    927                     if (stop_filter and stop_filter(p)) or \

/home/guedou/Projects/Scapy/scapy-issues/scapy/sessions.py in on_packet_received(self, pkt)
     45             self.lst.append(pkt)
     46         if self.prn:
---> 47             result = self.prn(pkt)
     48             if result is not None:
     49                 print(result)

/home/guedou/Projects/Scapy/scapy-issues/scapy/sendrecv.py in _process_packet(self, r)
    223                         self.notans -= 1
    224                     else:
--> 225                         if not hasattr(self.sentpkt, '_answered'):
    226                             self.notans -= 1
    227                         self.sentpkt._answered = 1

AttributeError: 'SndRcvHandler' object has no attribute 'sentpkt'
```


`v2.4.0`:
```
>>> conf.checkIPsrc = False ; sr(IPv6(dst="ff02::1")/ICMPv6EchoRequest(), multi=1, timeout=1)
Begin emission:
Finished sending 1 packets.
**.***...................
Received 25 packets, got 5 answers, remaining 0 packets
(<Results: TCP:0 UDP:0 ICMP:0 Other:5>,
 <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)
```


Discussion:
This is a possible fix:
```
diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py
index bc601c18..c1d81410 100644
--- a/scapy/sendrecv.py
+++ b/scapy/sendrecv.py
@@ -222,9 +222,9 @@ class SndRcvHandler(object):
                         del hlst[i]
                         self.notans -= 1
                     else:
-                        if not hasattr(self.sentpkt, '_answered'):
+                        if not hasattr(sentpkt, '_answered'):
                             self.notans -= 1
-                        self.sentpkt._answered = 1
+                        sentpkt._answered = 1
                     break
         if self.notans <= 0 and not self.multi:
             self.sniffer.stop(join=False)

```
My bad. I've added a lot of `self.` when introducing the classes.

Resolving patch:
diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py
--- a/scapy/sendrecv.py
+++ b/scapy/sendrecv.py
@@ -222,9 +222,9 @@ def _process_packet(self, r):
                         del hlst[i]
                         self.notans -= 1
                     else:
-                        if not hasattr(self.sentpkt, '_answered'):
+                        if not hasattr(sentpkt, '_answered'):
                             self.notans -= 1
-                        self.sentpkt._answered = 1
+                        sentpkt._answered = 1
                     break
         if self.notans <= 0 and not self.multi:
             self.sniffer.stop(join=False)


Test patch:
diff --git a/test/regression.uts b/test/regression.uts
--- a/test/regression.uts
+++ b/test/regression.uts
@@ -1578,6 +1578,17 @@ def _test():
 
 retry_test(_test)
 
+= Send & receive with multi
+~ netaccess IP ICMP
+def _test():
+    old_debug_dissector = conf.debug_dissector
+    conf.debug_dissector = False
+    ans, unans = sr(IP(dst=["8.8.8.8", "1.2.3.4"]) / ICMP(), timeout=2, multi=1)
+    conf.debug_dissector = old_debug_dissector
+    len(ans) == 1 and len(unans) == 1
+
+retry_test(_test)
+
 = Traceroute function
 ~ netaccess tcpdump
 * Let's test traceroute