Repository: secdev/scapy

Issue:
RawVal does not work with all field types
### Brief description

[RawVal](https://scapy.readthedocs.io/en/latest/api/scapy.fields.html#scapy.fields.RawVal) does not work with all field types (or at least with the FixedPointField type used for [NTP](https://scapy.readthedocs.io/en/latest/api/scapy.layers.ntp.html#scapy.layers.ntp.NTPHeader.fields_desc)).

### Scapy version

c406c7d98985c7925eacd22a06965fae04cab7b0

### Python version

3.8.10

### Operating system

5.15.0-41-generic

### Additional environment information

_No response_

### How to reproduce

To reproduce, simply try to use RawVal for a FixedPointField, for example:

`>>> NTP(orig=RawVal(b"\xe6}gt\x00\x00\x00\x00"))`

### Actual result

```
>>> NTP(orig=RawVal(b"\xe6}gt\x00\x00\x00\x00"))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
File /home/ytrewq/.local/lib/python3.8/site-packages/IPython/core/formatters.py:707, in PlainTextFormatter.__call__(self, obj)
    700 stream = StringIO()
    701 printer = pretty.RepresentationPrinter(stream, self.verbose,
    702     self.max_width, self.newline,
    703     max_seq_length=self.max_seq_length,
    704     singleton_pprinters=self.singleton_printers,
    705     type_pprinters=self.type_printers,
    706     deferred_pprinters=self.deferred_printers)
--> 707 printer.pretty(obj)
    708 printer.flush()
    709 return stream.getvalue()

File /home/ytrewq/.local/lib/python3.8/site-packages/IPython/lib/pretty.py:410, in RepresentationPrinter.pretty(self, obj)
    407                         return meth(obj, self, cycle)
    408                 if cls is not object \
    409                         and callable(cls.__dict__.get('__repr__')):
--> 410                     return _repr_pprint(obj, self, cycle)
    412     return _default_pprint(obj, self, cycle)
    413 finally:

File /home/ytrewq/.local/lib/python3.8/site-packages/IPython/lib/pretty.py:778, in _repr_pprint(obj, p, cycle)
    776 """A pprint that just redirects to the normal repr function."""
    777 # Find newlines and replace them with p.break_()
--> 778 output = repr(obj)
    779 lines = output.splitlines()
    780 with p.group():

File /home/ytrewq/.local/lib/python3.8/site-packages/scapy/packet.py:533, in Packet.__repr__(self)
    531     if isinstance(fval, (list, dict, set)) and len(fval) == 0:
    532         continue
--> 533     val = f.i2repr(self, fval)
    534 elif f.name in self.overloaded_fields:
    535     fover = self.overloaded_fields[f.name]

File /home/ytrewq/.local/lib/python3.8/site-packages/scapy/layers/ntp.py:80, in TimeStampField.i2repr(self, pkt, val)
     78 if val is None:
     79     return "--"
---> 80 val = self.i2h(pkt, val)
     81 if val < _NTP_BASETIME:
     82     return val

File /home/ytrewq/.local/lib/python3.8/site-packages/scapy/fields.py:3044, in FixedPointField.i2h(self, pkt, val)
   3041 def i2h(self, pkt, val):
   3042     # type: (Optional[Packet], int) -> EDecimal
   3043     # A bit of trickery to get precise floats
-> 3044     int_part = val >> self.frac_bits
   3045     pw = 2.0**self.frac_bits
   3046     frac_part = EDecimal(val & (1 << self.frac_bits) - 1)

TypeError: unsupported operand type(s) for >>: 'RawVal' and 'int'
```

### Expected result

Expected: no error. The example earlier should work exactly the same as doing this:

```
>>> NTP(orig=0x00000000e67d6774)
<NTPHeader  orig=Sat, 16 Jul 2022 16:36:04 +0000 |>
```

### Related resources

_No response_


Discussion:

Resolving patch:
diff --git a/scapy/packet.py b/scapy/packet.py
--- a/scapy/packet.py
+++ b/scapy/packet.py
@@ -174,14 +174,16 @@ def __init__(self,
                 value = fields.pop(fname)
             except KeyError:
                 continue
-            self.fields[fname] = self.get_field(fname).any2i(self, value)
+            self.fields[fname] = value if isinstance(value, RawVal) else \
+                self.get_field(fname).any2i(self, value)
         # The remaining fields are unknown
         for fname in fields:
             if fname in self.deprecated_fields:
                 # Resolve deprecated fields
                 value = fields[fname]
                 fname = self._resolve_alias(fname)
-                self.fields[fname] = self.get_field(fname).any2i(self, value)
+                self.fields[fname] = value if isinstance(value, RawVal) else \
+                    self.get_field(fname).any2i(self, value)
                 continue
             raise AttributeError(fname)
         if isinstance(post_transform, list):
@@ -453,7 +455,7 @@ def __getattr__(self, attr):
         except ValueError:
             return self.payload.__getattr__(attr)
         if fld is not None:
-            return fld.i2h(self, v)
+            return v if isinstance(v, RawVal) else fld.i2h(self, v)
         return v
 
     def setfieldval(self, attr, val):


Test patch:
diff --git a/test/scapy/layers/ntp.uts b/test/scapy/layers/ntp.uts
--- a/test/scapy/layers/ntp.uts
+++ b/test/scapy/layers/ntp.uts
@@ -1095,3 +1095,46 @@ assert(isinstance(p.data[0], NTPInfoIfStatsIPv4))
 assert(p.data[0].unaddr == "127.0.0.1")
 assert(p.data[0].unmask == "255.0.0.0")
 assert(p.data[0].ifname.startswith(b"lo"))
+
+############
+############
++ RawVal tests
+
+= Build an NTP packet using RawVal
+
+from decimal import Decimal
+
+precision = b"\xec"  # 236
+dispersion = b"\x00\x00\xf2\xce"  # 0.948455810546875
+time_stamp = b"\xe6}gt\x00\x00\x00\x00" # Sat, 16 Jul 2022 16:36:04 +0000
+
+pkt_1 = NTP(
+    precision=RawVal(precision),
+    dispersion=RawVal(dispersion),
+    orig=RawVal(time_stamp),
+    sent=RawVal(time_stamp),
+)
+
+assert(isinstance(pkt_1.precision, RawVal)), type(pkt_1.precision)
+assert(isinstance(pkt_1.dispersion, RawVal)), type(pkt_1.dispersion)
+assert(isinstance(pkt_1.orig, RawVal)), type(pkt_1.orig)
+assert(isinstance(pkt_1.sent, RawVal)), type(pkt_1.sent)
+
+assert(pkt_1.precision.val == precision), pkt_1.precision.val
+assert(pkt_1.dispersion.val == dispersion), pkt_1.dispersion.val
+assert(pkt_1.orig.val == time_stamp), pkt_1.orig.val
+assert(pkt_1.sent.val == time_stamp), pkt_1.sent.val
+
+time_stamp_hex = 0x00000000e67d6774
+pkt_2 = NTP(
+    precision=236,
+    dispersion=Decimal('0.948455810546875'),
+    orig=time_stamp_hex,
+    sent=time_stamp_hex
+)
+
+raw_pkt = (b"#\x02\n\xec\x00\x00\x00\x00\x00\x00\xf2\xce\x7f\x00\x00\x01\x00"
+           b"\x00\x00\x00\x00\x00\x00\x00\xe6}gt\x00\x00\x00\x00\x00\x00\x00"
+           b"\x00\x00\x00\x00\x00\xe6}gt\x00\x00\x00\x00")
+
+assert(raw(pkt_1) == raw(pkt_2) == raw_pkt)
\ No newline at end of file