Home | History | Annotate | Download | only in contrib
      1 # This file is part of Scapy
      2 # See http://www.secdev.org/projects/scapy for more informations
      3 # Copyright (C) Santiago Hernandez Ramos <shramos (at] protonmail.com>
      4 # This program is published under GPLv2 license
      5 
      6 
      7 from scapy.packet import Packet, bind_layers
      8 from scapy.fields import FieldLenField, BitEnumField, StrLenField, \
      9     ShortField, ConditionalField, ByteEnumField, ByteField, StrNullField
     10 from scapy.layers.inet import TCP
     11 from scapy.error import Scapy_Exception
     12 from scapy.compat import orb, chb
     13 
     14 
     15 # CUSTOM FIELDS
     16 
     17 # source: http://stackoverflow.com/a/43717630
     18 class VariableFieldLenField(FieldLenField):
     19     def addfield(self, pkt, s, val):
     20         val = self.i2m(pkt, val)
     21         data = []
     22         while val:
     23             if val > 127:
     24                 data.append(val & 127)
     25                 val /= 127
     26             else:
     27                 data.append(val)
     28                 lastoffset = len(data) - 1
     29                 data = b"".join(chb(val | (0 if i == lastoffset else 128))
     30                                for i, val in enumerate(data))
     31                 return s + data
     32             if len(data) > 3:
     33                 raise Scapy_Exception("%s: malformed length field" %
     34                                       self.__class__.__name__)
     35 
     36     def getfield(self, pkt, s):
     37         value = 0
     38         for offset, curbyte in enumerate(s):
     39             curbyte = orb(curbyte)
     40             value += (curbyte & 127) * (128 ** offset)
     41             if curbyte & 128 == 0:
     42                 return s[offset + 1:], value
     43             if offset > 2:
     44                 raise Scapy_Exception("%s: malformed length field" %
     45                                       self.__class__.__name__)
     46 
     47 
     48 # LAYERS
     49 
     50 CONTROL_PACKET_TYPE = {1: 'CONNECT',
     51                        2: 'CONNACK',
     52                        3: 'PUBLISH',
     53                        4: 'PUBACK',
     54                        5: 'PUBREC',
     55                        6: 'PUBREL',
     56                        7: 'PUBCOMP',
     57                        8: 'SUBSCRIBE',
     58                        9: 'SUBACK',
     59                        10: 'UNSUBSCRIBE',
     60                        11: 'UNSUBACK',
     61                        12: 'PINGREQ',
     62                        13: 'PINGRESP',
     63                        14: 'DISCONNECT'}
     64 
     65 
     66 QOS_LEVEL = {0: 'At most once delivery',
     67              1: 'At least once delivery',
     68              2: 'Exactly once delivery'}
     69 
     70 
     71 # source: http://stackoverflow.com/a/43722441
     72 class MQTT(Packet):
     73     name = "MQTT fixed header"
     74     fields_desc = [
     75         BitEnumField("type", 1, 4, CONTROL_PACKET_TYPE),
     76         BitEnumField("DUP", 0, 1, {0: 'Disabled',
     77                                    1: 'Enabled'}),
     78         BitEnumField("QOS", 0, 2, QOS_LEVEL),
     79         BitEnumField("RETAIN", 0, 1, {0: 'Disabled',
     80                                       1: 'Enabled'}),
     81         # Since the size of the len field depends on the next layer, we need
     82         # to "cheat" with the length_of parameter and use adjust parameter to
     83         # calculate the value.
     84         VariableFieldLenField("len", None, length_of="len",
     85                               adjust=lambda pkt, x: len(pkt.payload),),
     86     ]
     87 
     88 
     89 class MQTTConnect(Packet):
     90     name = "MQTT connect"
     91     fields_desc = [
     92         FieldLenField("length", None, length_of="protoname"),
     93         StrLenField("protoname", "",
     94                     length_from=lambda pkt: pkt.length),
     95         ByteField("protolevel", 0),
     96         BitEnumField("usernameflag", 0, 1, {0: 'Disabled',
     97                                             1: 'Enabled'}),
     98         BitEnumField("passwordflag", 0, 1, {0: 'Disabled',
     99                                             1: 'Enabled'}),
    100         BitEnumField("willretainflag", 0, 1, {0: 'Disabled',
    101                                               1: 'Enabled'}),
    102         BitEnumField("willQOSflag", 0, 2, QOS_LEVEL),
    103         BitEnumField("willflag", 0, 1, {0: 'Disabled',
    104                                         1: 'Enabled'}),
    105         BitEnumField("cleansess", 0, 1, {0: 'Disabled',
    106                                          1: 'Enabled'}),
    107         BitEnumField("reserved", 0, 1, {0: 'Disabled',
    108                                         1: 'Enabled'}),
    109         ShortField("klive", 0),
    110         FieldLenField("clientIdlen", None, length_of="clientId"),
    111         StrLenField("clientId", "",
    112                     length_from=lambda pkt: pkt.clientIdlen),
    113         # Payload with optional fields depending on the flags
    114         ConditionalField(FieldLenField("wtoplen", None, length_of="willtopic"),
    115                          lambda pkt: pkt.willflag == 1),
    116         ConditionalField(StrLenField("willtopic", "",
    117                                      length_from=lambda pkt: pkt.wtoplen),
    118                          lambda pkt: pkt.willflag == 1),
    119         ConditionalField(FieldLenField("wmsglen", None, length_of="willmsg"),
    120                          lambda pkt: pkt.willflag == 1),
    121         ConditionalField(StrLenField("willmsg", "",
    122                                      length_from=lambda pkt: pkt.wmsglen),
    123                          lambda pkt: pkt.willflag == 1),
    124         ConditionalField(FieldLenField("userlen", None, length_of="username"),
    125                          lambda pkt: pkt.usernameflag == 1),
    126         ConditionalField(StrLenField("username", "",
    127                                      length_from=lambda pkt: pkt.userlen),
    128                          lambda pkt: pkt.usernameflag == 1),
    129         ConditionalField(FieldLenField("passlen", None, length_of="password"),
    130                          lambda pkt: pkt.passwordflag == 1),
    131         ConditionalField(StrLenField("password", "",
    132                                      length_from=lambda pkt: pkt.passlen),
    133                          lambda pkt: pkt.passwordflag == 1),
    134     ]
    135 
    136 
    137 RETURN_CODE = {0: 'Connection Accepted',
    138                1: 'Unacceptable protocol version',
    139                2: 'Identifier rejected',
    140                3: 'Server unavailable',
    141                4: 'Bad username/password',
    142                5: 'Not authorized'}
    143 
    144 
    145 class MQTTConnack(Packet):
    146     name = "MQTT connack"
    147     fields_desc = [
    148         ByteField("sessPresentFlag", 0),
    149         ByteEnumField("retcode", 0, RETURN_CODE),
    150         # this package has not payload
    151     ]
    152 
    153 
    154 class MQTTPublish(Packet):
    155     name = "MQTT publish"
    156     fields_desc = [
    157         FieldLenField("length", None, length_of="topic"),
    158         StrLenField("topic", "",
    159                     length_from=lambda pkt: pkt.length),
    160         ConditionalField(ShortField("msgid", None),
    161                          lambda pkt: (pkt.underlayer.QOS == 1
    162                                       or pkt.underlayer.QOS == 2)),
    163         StrLenField("value", "",
    164                     length_from=lambda pkt: (pkt.underlayer.len -
    165                                              pkt.length - 2)),
    166     ]
    167 
    168 
    169 class MQTTPuback(Packet):
    170     name = "MQTT puback"
    171     fields_desc = [
    172         ShortField("msgid", None),
    173         ]
    174 
    175 
    176 class MQTTPubrec(Packet):
    177     name = "MQTT pubrec"
    178     fields_desc = [
    179         ShortField("msgid", None),
    180         ]
    181 
    182 
    183 class MQTTPubrel(Packet):
    184     name = "MQTT pubrel"
    185     fields_desc = [
    186         ShortField("msgid", None),
    187         ]
    188 
    189 
    190 class MQTTPubcomp(Packet):
    191     name = "MQTT pubcomp"
    192     fields_desc = [
    193         ShortField("msgid", None),
    194         ]
    195 
    196 
    197 class MQTTSubscribe(Packet):
    198     name = "MQTT subscribe"
    199     fields_desc = [
    200         ShortField("msgid", None),
    201         FieldLenField("length", None, length_of="topic"),
    202         StrLenField("topic", "",
    203                     length_from=lambda pkt: pkt.length),
    204         ByteEnumField("QOS", 0, QOS_LEVEL),
    205         ]
    206 
    207 
    208 ALLOWED_RETURN_CODE = {0: 'Success',
    209                        1: 'Success',
    210                        2: 'Success',
    211                        128: 'Failure'}
    212 
    213 
    214 class MQTTSuback(Packet):
    215     name = "MQTT suback"
    216     fields_desc = [
    217         ShortField("msgid", None),
    218         ByteEnumField("retcode", None, ALLOWED_RETURN_CODE)
    219         ]
    220 
    221 
    222 class MQTTUnsubscribe(Packet):
    223     name = "MQTT unsubscribe"
    224     fields_desc = [
    225         ShortField("msgid", None),
    226         StrNullField("payload", "")
    227         ]
    228 
    229 
    230 class MQTTUnsuback(Packet):
    231     name = "MQTT unsuback"
    232     fields_desc = [
    233         ShortField("msgid", None)
    234         ]
    235 
    236 
    237 # LAYERS BINDINGS
    238 
    239 bind_layers(TCP, MQTT, sport=1883)
    240 bind_layers(TCP, MQTT, dport=1883)
    241 bind_layers(MQTT, MQTTConnect, type=1)
    242 bind_layers(MQTT, MQTTConnack, type=2)
    243 bind_layers(MQTT, MQTTPublish, type=3)
    244 bind_layers(MQTT, MQTTPuback, type=4)
    245 bind_layers(MQTT, MQTTPubrec, type=5)
    246 bind_layers(MQTT, MQTTPubrel, type=6)
    247 bind_layers(MQTT, MQTTPubcomp, type=7)
    248 bind_layers(MQTT, MQTTSubscribe, type=8)
    249 bind_layers(MQTT, MQTTSuback, type=9)
    250 bind_layers(MQTT, MQTTUnsubscribe, type=10)
    251 bind_layers(MQTT, MQTTUnsuback, type=11)
    252 bind_layers(MQTTConnect, MQTT)
    253 bind_layers(MQTTConnack, MQTT)
    254 bind_layers(MQTTPublish, MQTT)
    255 bind_layers(MQTTPuback, MQTT)
    256 bind_layers(MQTTPubrec, MQTT)
    257 bind_layers(MQTTPubrel, MQTT)
    258 bind_layers(MQTTPubcomp, MQTT)
    259 bind_layers(MQTTSubscribe, MQTT)
    260 bind_layers(MQTTSuback, MQTT)
    261 bind_layers(MQTTUnsubscribe, MQTT)
    262 bind_layers(MQTTUnsuback, MQTT)
    263