Repository: secdev/scapy

Issue:
mistaken msglen in MQTTPublish message
### Brief description

I noticed that the StrLenField of value in MQTTPublish packets is defined as
```python
StrLenField("value", "",
                    length_from=lambda pkt: (pkt.underlayer.len -
                                             pkt.length - 2)),
```
However, if the QoS of the publish message is 1 or 2, because of the field of msgid,  I guess it should be pkt.underlayer.len - pkt.length - 4, as I met this problem when I was trying to parse a packet that contains two MQTT Publish messages. 
![image](https://user-images.githubusercontent.com/79267237/161885502-3e24b59c-95a7-4761-a642-1e1aa135bb45.png)
I tried to fix it with codes as followed. Perhaps it could help. 
```python
StrLenField("value", "",
                    length_from=lambda pkt: pkt.underlayer.len -pkt.length - 2 if pkt.underlayer.QOS == 0 
                                            else pkt.underlayer.len -pkt.length - 4)
```



### Scapy version

2.4.5

### Python version

3.8

### Operating system

Win11

### Additional environment information

_No response_

### How to reproduce

Sniff a packet with two MQTT Publish messages......

### Actual result

_No response_

### Expected result

_No response_

### Related resources

_No response_


Discussion:
Could you share a packet that triggers this behavior?
> Could you share a packet that triggers this behavior?

If a client disconnects to the MQTT broker with unacknowledged PUBLISH messages and connects to the broker with CLEANSESSION=0, the broker will send the duplicate PUBLISH messages at the same time, which will be packaged in a single TCP packet.
I tried to simulate such packet with the codes below:
```python
p1 = MQTT(
            QOS=1
        ) / MQTTPublish(
            topic=topicC,
            msgid=1234,
            value="msg1"
        )
p2 = MQTT(
            QOS=1
        ) / MQTTPublish(
            topic=topicC,
            msgid=1235,
            value="msg2"
        )
socket.send(raw(p1)+raw(p2))
```
And then I use sniff() function to capture the packet:
![image](https://user-images.githubusercontent.com/79267237/162600004-74729838-1f89-4dc2-b872-516132a65d0a.png)
The first 2 bytes of p2 are charged into publish value of p1.

Resolving patch:
diff --git a/scapy/contrib/mqtt.py b/scapy/contrib/mqtt.py
--- a/scapy/contrib/mqtt.py
+++ b/scapy/contrib/mqtt.py
@@ -192,8 +192,9 @@ class MQTTPublish(Packet):
                          lambda pkt: (pkt.underlayer.QOS == 1 or
                                       pkt.underlayer.QOS == 2)),
         StrLenField("value", "",
-                    length_from=lambda pkt: (pkt.underlayer.len -
-                                             pkt.length - 2)),
+                    length_from=lambda pkt: pkt.underlayer.len - pkt.length - 2
+                    if pkt.underlayer.QOS == 0 else
+                    pkt.underlayer.len - pkt.length - 4)
     ]
 
 


Test patch:
diff --git a/test/contrib/mqtt.uts b/test/contrib/mqtt.uts
--- a/test/contrib/mqtt.uts
+++ b/test/contrib/mqtt.uts
@@ -31,6 +31,27 @@ assert(publish[MQTTPublish].length == 4)
 assert(publish[MQTTPublish].topic == b'test')
 assert(publish[MQTTPublish].value == b'test')
 
+= MQTTPublish
+
+topicC = "testtopic/command"
+
+p1 = MQTT(
+            QOS=1
+        ) / MQTTPublish(
+            topic=topicC,
+            msgid=1234,
+            value="msg1"
+        )
+p2 = MQTT(
+            QOS=1
+        ) / MQTTPublish(
+            topic=topicC,
+            msgid=1235,
+            value="msg2"
+        )
+
+p = MQTT(raw(p1 / p2))
+assert p[1].msgid == 1234
 
 = MQTTConnect, packet instantiation
 c = MQTT()/MQTTConnect(clientIdlen=5, clientId='newid')
@@ -160,4 +181,4 @@ assert MQTTUnsubscribe in u and len(u.topics) == 2 and u.topics[1].topic == b"c/
 
 = MQTTSubscribe
 u = MQTT(b'\x82\x10\x00\x01\x00\x03\x61\x2F\x62\x02\x00\x03\x63\x2F\x64\x00')
-assert MQTTSubscribe in u and len(u.topics) == 2 and u.topics[1].topic == b"c/d"
+assert MQTTSubscribe in u and len(u.topics) == 2 and u.topics[1].topic == b"c/d"
\ No newline at end of file