Repository: secdev/scapy

Issue:
Broken Packet.time arithmetic involving float
#### Brief description
The introduction of `EDecimal` in  b3bfc9b (#2352) broke any arithmetic involving `Packet.time` and `float` values, if the packet was read from a pcap file.

#### Environment

-   Scapy version: `2.4.3.dev242`, dcfb63d0
-   Python version: `2.7.17`
-   Operating System: `Ubuntu 18.04`, but affects all

#### How to reproduce

```
# Create a packet, write to pcap and read it back in. This creates a packet
# that uses EDecimal as type of its time attribute due to fix #2352.
# Of course one could also directly instantiate an EDecimal object
p = Ether()/IP()/TCP()
wrpcap('sample.pcap', p)
ps = rdpcap('sample.pcap')

# Any arithmetic with float results in an exception.
# For EDecimal on the left a TypeError("Cannot convert %r to Decimal" % value) is thrown
# due to Decimal not supporting direct arithmetic with float
x = ps[0].time + 2.5
x = ps[0].time - 2.5
x = ps[0].time * 2.5

# For EDecimal on the right a 'TypeError: unsupported operand type(s)' is thrown
# due to EDecimal not defining right hand side operators
x = 2.5 + ps[0].time
x = 2.5 - ps[0].time
x = 2.5 * ps[0].time
```

#### Actual result

```
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/scapy-2.4.3.dev227-py2.7.egg/scapy/utils.py", line 67, in __mul__
    return EDecimal(Decimal.__mul__(self, other, **kwargs))
  File "/usr/lib/python2.7/decimal.py", line 657, in __new__
    raise TypeError("Cannot convert %r to Decimal" % value)
TypeError: Cannot convert NotImplemented to Decimal
```

```
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for *: 'float' and 'EDecimal'
```

#### Expected result

an `EDecimal` containing the result of the arithmetic operation



Discussion:

Resolving patch:
diff --git a/scapy/utils.py b/scapy/utils.py
--- a/scapy/utils.py
+++ b/scapy/utils.py
@@ -54,35 +54,57 @@ def issubtype(x, t):
 class EDecimal(Decimal):
     """Extended Decimal
 
-    This implement comparison with float for backward compatibility
+    This implements arithmetic and comparison with float for
+    backward compatibility
     """
 
     def __add__(self, other, **kwargs):
-        return EDecimal(Decimal.__add__(self, other, **kwargs))
+        return EDecimal(Decimal.__add__(self, Decimal(other), **kwargs))
+
+    def __radd__(self, other, **kwargs):
+        return EDecimal(Decimal.__add__(self, Decimal(other), **kwargs))
 
     def __sub__(self, other, **kwargs):
-        return EDecimal(Decimal.__sub__(self, other, **kwargs))
+        return EDecimal(Decimal.__sub__(self, Decimal(other), **kwargs))
+
+    def __rsub__(self, other, **kwargs):
+        return EDecimal(Decimal.__rsub__(self, Decimal(other), **kwargs))
 
     def __mul__(self, other, **kwargs):
-        return EDecimal(Decimal.__mul__(self, other, **kwargs))
+        return EDecimal(Decimal.__mul__(self, Decimal(other), **kwargs))
+
+    def __rmul__(self, other, **kwargs):
+        return EDecimal(Decimal.__mul__(self, Decimal(other), **kwargs))
 
     def __truediv__(self, other, **kwargs):
-        return EDecimal(Decimal.__truediv__(self, other, **kwargs))
+        return EDecimal(Decimal.__truediv__(self, Decimal(other), **kwargs))
 
     def __floordiv__(self, other, **kwargs):
-        return EDecimal(Decimal.__floordiv__(self, other, **kwargs))
+        return EDecimal(Decimal.__floordiv__(self, Decimal(other), **kwargs))
 
     def __div__(self, other, **kwargs):
-        return EDecimal(Decimal.__div__(self, other, **kwargs))
+        return EDecimal(Decimal.__div__(self, Decimal(other), **kwargs))
+
+    def __rdiv__(self, other, **kwargs):
+        return EDecimal(Decimal.__rdiv__(self, Decimal(other), **kwargs))
 
     def __mod__(self, other, **kwargs):
-        return EDecimal(Decimal.__mod__(self, other, **kwargs))
+        return EDecimal(Decimal.__mod__(self, Decimal(other), **kwargs))
+
+    def __rmod__(self, other, **kwargs):
+        return EDecimal(Decimal.__rmod__(self, Decimal(other), **kwargs))
 
     def __divmod__(self, other, **kwargs):
-        return EDecimal(Decimal.__divmod__(self, other, **kwargs))
+        return EDecimal(Decimal.__divmod__(self, Decimal(other), **kwargs))
+
+    def __rdivmod__(self, other, **kwargs):
+        return EDecimal(Decimal.__rdivmod__(self, Decimal(other), **kwargs))
 
     def __pow__(self, other, **kwargs):
-        return EDecimal(Decimal.__pow__(self, other, **kwargs))
+        return EDecimal(Decimal.__pow__(self, Decimal(other), **kwargs))
+
+    def __rpow__(self, other, **kwargs):
+        return EDecimal(Decimal.__rpow__(self, Decimal(other), **kwargs))
 
     def __eq__(self, other, **kwargs):
         return super(EDecimal, self).__eq__(other) or float(self) == other


Test patch: