Skip to content

Commit b60b29b

Browse files
authored
Fix #4717 & cleanup NTP control (#4791)
1 parent 8ddf371 commit b60b29b

File tree

2 files changed

+117
-172
lines changed

2 files changed

+117
-172
lines changed

scapy/layers/ntp.py

Lines changed: 58 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
ByteEnumField,
2020
ByteField,
2121
ConditionalField,
22+
FieldLenField,
2223
FieldListField,
2324
FixedPointField,
2425
FlagsField,
@@ -28,15 +29,16 @@
2829
LEIntField,
2930
LEShortField,
3031
MayEnd,
32+
MultipleTypeField,
3133
PacketField,
32-
PacketLenField,
3334
PacketListField,
3435
PadField,
3536
ShortField,
3637
SignedByteField,
3738
StrField,
3839
StrFixedLenEnumField,
3940
StrFixedLenField,
41+
StrLenField,
4042
XByteField,
4143
XStrFixedLenField,
4244
)
@@ -610,18 +612,6 @@ def __init__(self, details):
610612
}
611613

612614

613-
class NTPStatusPacket(Packet):
614-
"""
615-
Packet handling a non specific status word.
616-
"""
617-
618-
name = "status"
619-
fields_desc = [ShortField("status", 0)]
620-
621-
def extract_padding(self, s):
622-
return b"", s
623-
624-
625615
class NTPSystemStatusPacket(Packet):
626616

627617
"""
@@ -691,55 +681,6 @@ def extract_padding(self, s):
691681
return b"", s
692682

693683

694-
class NTPControlStatusField(PacketField):
695-
"""
696-
This field provides better readability for the "status" field.
697-
"""
698-
699-
#########################################################################
700-
#
701-
# RFC 1305
702-
#########################################################################
703-
#
704-
# Appendix B.3. Commands // ntpd source code: ntp_control.h
705-
#########################################################################
706-
#
707-
708-
def m2i(self, pkt, m):
709-
ret = None
710-
association_id = struct.unpack("!H", m[2:4])[0]
711-
712-
if pkt.err == 1:
713-
ret = NTPErrorStatusPacket(m)
714-
715-
# op_code == CTL_OP_READSTAT
716-
elif pkt.op_code == 1:
717-
if association_id != 0:
718-
ret = NTPPeerStatusPacket(m)
719-
else:
720-
ret = NTPSystemStatusPacket(m)
721-
722-
# op_code == CTL_OP_READVAR
723-
elif pkt.op_code == 2:
724-
if association_id != 0:
725-
ret = NTPPeerStatusPacket(m)
726-
else:
727-
ret = NTPSystemStatusPacket(m)
728-
729-
# op_code == CTL_OP_WRITEVAR
730-
elif pkt.op_code == 3:
731-
ret = NTPStatusPacket(m)
732-
733-
# op_code == CTL_OP_READCLOCK or op_code == CTL_OP_WRITECLOCK
734-
elif pkt.op_code == 4 or pkt.op_code == 5:
735-
ret = NTPClockStatusPacket(m)
736-
737-
else:
738-
ret = NTPStatusPacket(m)
739-
740-
return ret
741-
742-
743684
class NTPPeerStatusDataPacket(Packet):
744685
"""
745686
Packet handling the data field when op_code is CTL_OP_READSTAT
@@ -752,97 +693,87 @@ class NTPPeerStatusDataPacket(Packet):
752693
PacketField("peer_status", NTPPeerStatusPacket(), NTPPeerStatusPacket),
753694
]
754695

696+
def extract_padding(self, s):
697+
return b"", s
755698

756-
class NTPControlDataPacketLenField(PacketLenField):
757699

700+
class NTPControlStatusField(PacketField):
758701
"""
759-
PacketField handling the "data" field of NTP control messages.
702+
The various types of the "status" field.
760703
"""
761-
704+
# RFC 9327 sect 3
762705
def m2i(self, pkt, m):
763-
ret = None
764-
if not m:
765-
return ret
766-
767-
# op_code == CTL_OP_READSTAT
768-
if pkt.op_code == 1:
769-
if pkt.association_id == 0:
770-
# Data contains association ID and peer status
771-
ret = NTPPeerStatusDataPacket(m)
772-
else:
773-
ret = conf.raw_layer(m)
774-
else:
775-
ret = conf.raw_layer(m)
776-
777-
return ret
706+
association_id = struct.unpack("!H", m[2:4])[0]
778707

779-
def getfield(self, pkt, s):
780-
length = self.length_from(pkt)
781-
i = None
782-
if length > 0:
783-
# RFC 1305
784-
# The maximum number of data octets is 468.
785-
#
786-
# include/ntp_control.h
787-
# u_char data[480 + MAX_MAC_LEN]; /* data + auth */
788-
#
789-
# Set the minimum length to 480 - 468
790-
length = max(12, length)
791-
if length % 4:
792-
length += (4 - length % 4)
793-
try:
794-
i = self.m2i(pkt, s[:length])
795-
except Exception:
796-
if conf.debug_dissector:
797-
raise
798-
i = conf.raw_layer(load=s[:length])
799-
return s[length:], i
708+
if pkt.err == 1:
709+
return NTPErrorStatusPacket(m)
710+
elif pkt.op_code in [4, 5]: # Read/write clock
711+
return NTPClockStatusPacket(m)
712+
else:
713+
if association_id != 0:
714+
return NTPPeerStatusPacket(m)
715+
else:
716+
return NTPSystemStatusPacket(m)
800717

801718

802719
class NTPControl(NTP):
803720
"""
804721
Packet handling NTP mode 6 / "Control" messages.
805722
"""
806-
807-
#########################################################################
808-
#
809-
# RFC 1305
810-
#########################################################################
811-
#
812-
# Appendix B.3. Commands // ntpd source code: ntp_control.h
813-
#########################################################################
814-
#
815-
816-
name = "Control message"
723+
deprecated_fields = {
724+
"status_word": ("status", "2.6.2"),
725+
}
726+
# RFC 9327 sect 2
727+
name = "NTP Control message"
817728
match_subclass = True
818729
fields_desc = [
819-
BitField("zeros", 0, 2),
730+
BitEnumField("leap", 0, 2, _leap_indicator),
820731
BitField("version", 2, 3),
821732
BitEnumField("mode", 6, 3, _ntp_modes),
822733
BitField("response", 0, 1),
823734
BitField("err", 0, 1),
824735
BitField("more", 0, 1),
825736
BitEnumField("op_code", 0, 5, _op_codes),
826737
ShortField("sequence", 0),
827-
ConditionalField(NTPControlStatusField(
828-
"status_word", "", Packet), lambda p: p.response == 1),
829-
ConditionalField(ShortField("status", 0), lambda p: p.response == 0),
738+
MultipleTypeField(
739+
[
740+
(
741+
ShortField("status", 0),
742+
lambda pkt: pkt.response == 0 or pkt.op_code in [6, 7]
743+
)
744+
],
745+
NTPControlStatusField("status", NTPSystemStatusPacket(), None),
746+
),
830747
ShortField("association_id", 0),
831748
ShortField("offset", 0),
832-
ShortField("count", None),
833-
MayEnd(NTPControlDataPacketLenField(
834-
"data", "", Packet, length_from=lambda p: p.count)),
749+
FieldLenField("count", None, length_of="data"),
750+
MayEnd(
751+
PadField(
752+
MultipleTypeField(
753+
# RFC 1305
754+
[
755+
(
756+
PacketListField(
757+
"data",
758+
"",
759+
NTPPeerStatusDataPacket,
760+
length_from=lambda p: p.count,
761+
),
762+
lambda pkt: (
763+
pkt.response and
764+
pkt.op_code == 1 and
765+
pkt.association_id == 0
766+
)
767+
),
768+
],
769+
StrLenField("data", "", length_from=lambda pkt: pkt.count),
770+
),
771+
align=4
772+
)
773+
),
835774
PacketField("authenticator", "", NTPAuthenticator),
836775
]
837776

838-
def post_build(self, p, pay):
839-
if self.count is None:
840-
length = 0
841-
if self.data:
842-
length = len(self.data)
843-
p = p[:11] + struct.pack("!H", length) + p[13:]
844-
return p + pay
845-
846777

847778
##############################################################################
848779
# Private (mode 7)

0 commit comments

Comments
 (0)