Repository: secdev/scapy

Issue:
TypeError: 'OSError' object is not subscriptable
When I running a python script using scapy(v2.4.0) to send large packets, got the following error:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/scapy/arch/linux.py", line 486, in send
    self.outs.sendto(sx, sdto)
OSError: [Errno 90] Message too long

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "./fuzz.py", line 81, in <module>
    main()
  File "./fuzz.py", line 77, in main
    verbose=0)
  File "/usr/local/lib/python3.6/site-packages/scapy/sendrecv.py", line 302, in send
    realtime=realtime, return_packets=return_packets)
  File "/usr/local/lib/python3.6/site-packages/scapy/sendrecv.py", line 276, in __gen_send
    s.send(p)
  File "/usr/local/lib/python3.6/site-packages/scapy/arch/linux.py", line 488, in send
    if msg[0] == 22 and len(sx) < conf.min_pkt_size:
TypeError: 'OSError' object is not subscriptable

Please help take a look.


Discussion:
Hi,

Please share a small and clear example / piece of code (please as small as possible), that reproduces the issue.

Thanks !
Prepare 2 machines which could ping each other successfully. Running the following program on one machine should be enough.
the command is -  python3 reproducer.py -h <peer_ip> <peer port> 
The content of the reproducer:
```python
#! /usr/bin/python

import sys
import getopt
import time
from scapy.all import *

from scapy.layers.sctp import _SCTPChunkParam
from scapy.layers.sctp import sctpchunkparamtypescls
from scapy.layers.sctp import sctpchunkparamtypes

class SCTPChunkParamIPAddr(_SCTPChunkParam, Packet):
    fields_desc = [ ShortEnumField("type", 6, sctpchunkparamtypes),
                    ShortField("len", 4), ]

def main():
    opts, _ = getopt.getopt(sys.argv[1:], "h:p:", ["host", "port"])
    for opt, arg in opts:
        if opt in ("-h", "--host"):
            target_host = arg
        elif opt in ("-p", "--port"):
            target_port = int(arg)

    sctp_header = (IP(dst=target_host, flags="DF")/
        SCTP(sport=RandShort(),dport=target_port,tag=0))

    send(sctp_header / SCTPChunkInit(init_tag = 1,
        a_rwnd = 4096,
        n_out_streams = 5,
        n_in_streams = 5,
        init_tsn = 1,
        params = [SCTPChunkParamIPAddr()]*500))

if __name__ == "__main__":
    main()
```
Thanks for sharing. Could you reduce the issue to 10 lines or so, on a single machine?

Please set MTU of device 'lo' to be 1500 on Linux system first.
`ip link set dev lo mtu 1500`
Then run command as below:
`python3 reproducer.py -h 127.0.0.1 -p 10000`

Don't forget to restore lo mtu to be original value(65536) after testing.
Here is a way to reproduce this error:
```
from scapy.all import *
import unittest.mock as mock

@mock.patch("scapy.arch.linux.socket.socket.sendto")
def test_L3PacketSocket_send_python3(mock_sendto):
    mock_sendto.side_effect = OSError(22, 2807)
    l3ps = L3PacketSocket()
    l3ps.send(IP(dst="8.8.8.8")/ICMP())

test_L3PacketSocket_send_python3()
```

In Python3, `OSError` is raised and should be handled slightly differently at https://github.com/secdev/scapy/blob/master/scapy/arch/linux.py#L568. A fix is on the way.


Resolving patch:
diff --git a/scapy/arch/linux.py b/scapy/arch/linux.py
--- a/scapy/arch/linux.py
+++ b/scapy/arch/linux.py
@@ -523,7 +523,7 @@ def send(self, x):
         try:
             return SuperSocket.send(self, x)
         except socket.error as msg:
-            if msg[0] == 22 and len(x) < conf.min_pkt_size:
+            if msg.errno == 22 and len(x) < conf.min_pkt_size:
                 padding = b"\x00" * (conf.min_pkt_size - len(x))
                 if isinstance(x, Packet):
                     return SuperSocket.send(self, x / Padding(load=padding))
@@ -565,9 +565,9 @@ def send(self, x):
         try:
             self.outs.sendto(sx, sdto)
         except socket.error as msg:
-            if msg[0] == 22 and len(sx) < conf.min_pkt_size:
+            if msg.errno == 22 and len(sx) < conf.min_pkt_size:
                 self.outs.send(sx + b"\x00" * (conf.min_pkt_size - len(sx)))
-            elif conf.auto_fragment and msg[0] == 90:
+            elif conf.auto_fragment and msg.errno == 90:
                 for p in x.fragment():
                     self.outs.sendto(raw(ll(p)), sdto)
             else:
diff --git a/scapy/as_resolvers.py b/scapy/as_resolvers.py
--- a/scapy/as_resolvers.py
+++ b/scapy/as_resolvers.py
@@ -126,7 +126,8 @@ def resolve(self, *ips):
             try:
                 res = ASres.resolve(*todo)
             except socket.error as e:
-                if e[0] in [errno.ECONNREFUSED, errno.ETIMEDOUT, errno.ECONNRESET]:  # noqa: E501
+                if e.errno in [errno.ECONNREFUSED, errno.ETIMEDOUT,
+                   errno.ECONNRESET]:
                     continue
             resolved = [ip for ip, asn, desc in res]
             todo = [ip for ip in todo if ip not in resolved]


Test patch:
diff --git a/test/linux.uts b/test/linux.uts
--- a/test/linux.uts
+++ b/test/linux.uts
@@ -330,3 +330,17 @@ def test_read_routes(mock_ioctl):
     read_routes()
 
 test_read_routes()
+
+
+= L3PacketSocket sendto exception
+~ linux needs_root
+
+if six.PY3:
+    import mock, six, socket
+    @mock.patch("scapy.arch.linux.socket.socket.sendto")
+    def test_L3PacketSocket_sendto_python3(mock_sendto):
+        mock_sendto.side_effect = OSError(22, 2807)
+        l3ps = L3PacketSocket()
+        l3ps.send(IP(dst="8.8.8.8")/ICMP())
+        return True
+    assert test_L3PacketSocket_sendto_python3()