diff options
author | George Hazan <ghazan@miranda.im> | 2019-12-23 19:46:00 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2019-12-23 19:46:00 +0300 |
commit | 22a846c74f1903eb9b881b0d3b4615b249960be9 (patch) | |
tree | 56d18d9d6cc94a252a1f12703388f2b3e258499b /protocols/Facebook/src | |
parent | e016a7802692a2ccfba34c2505dbd4b14a39d715 (diff) |
Facebook: presence packet parsing
Diffstat (limited to 'protocols/Facebook/src')
-rw-r--r-- | protocols/Facebook/src/mqtt.cpp | 170 | ||||
-rw-r--r-- | protocols/Facebook/src/mqtt.h | 27 | ||||
-rw-r--r-- | protocols/Facebook/src/proto.h | 3 | ||||
-rw-r--r-- | protocols/Facebook/src/server.cpp | 82 | ||||
-rw-r--r-- | protocols/Facebook/src/stdafx.h | 1 | ||||
-rw-r--r-- | protocols/Facebook/src/thrift.cpp | 306 |
6 files changed, 459 insertions, 130 deletions
diff --git a/protocols/Facebook/src/mqtt.cpp b/protocols/Facebook/src/mqtt.cpp index 72ed222079..c80544176b 100644 --- a/protocols/Facebook/src/mqtt.cpp +++ b/protocols/Facebook/src/mqtt.cpp @@ -20,36 +20,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "stdafx.h" -static uint8_t encodeType(int type) -{ - switch (type) { - case FB_THRIFT_TYPE_BOOL: - return 2; - case FB_THRIFT_TYPE_BYTE: - return 3; - case FB_THRIFT_TYPE_I16: - return 4; - case FB_THRIFT_TYPE_I32: - return 5; - case FB_THRIFT_TYPE_I64: - return 6; - case FB_THRIFT_TYPE_DOUBLE: - return 7; - case FB_THRIFT_TYPE_STRING: - return 8; - case FB_THRIFT_TYPE_LIST: - return 9; - case FB_THRIFT_TYPE_SET: - return 10; - case FB_THRIFT_TYPE_MAP: - return 11; - case FB_THRIFT_TYPE_STRUCT: - return 12; - default: - return 0; - } -} - uint8_t *FacebookProto::doZip(size_t cbData, const void *pData, size_t &cbRes) { size_t dataSize = cbData + 100; @@ -101,91 +71,7 @@ uint8_t *FacebookProto::doUnzip(size_t cbData, const void *pData, size_t &cbRes) } ///////////////////////////////////////////////////////////////////////////////////////// - -FbThrift& FbThrift::operator<<(uint8_t value) -{ - m_buf.append(&value, 1); - return *this; -} - -FbThrift& FbThrift::operator<<(const char *str) -{ - size_t len = mir_strlen(str); - writeIntV(len); - m_buf.append((void*)str, len); - return *this; -} - -void FbThrift::writeBool(bool bValue) -{ - uint8_t b = (bValue) ? 0x11 : 0x12; - m_buf.append(&b, 1); -} - -void FbThrift::writeBuf(const void *pData, size_t cbLen) -{ - m_buf.append((void*)pData, cbLen); -} - -void FbThrift::writeField(int iType) -{ - uint8_t type = encodeType(iType) + 0x10; - m_buf.append(&type, 1); -} - -void FbThrift::writeField(int iType, int id, int lastid) -{ - uint8_t type = encodeType(iType); - uint8_t diff = uint8_t(id - lastid); - if (diff > 0x0F) { - m_buf.append(&type, 1); - writeInt64(id); - } - else { - type += (diff << 4); - m_buf.append(&type, 1); - } -} - -void FbThrift::writeList(int iType, int size) -{ - uint8_t type = encodeType(iType); - if (size > 14) { - writeIntV(size); - *this << (type | 0xF0); - } - else *this << (type | (size << 4)); -} - -void FbThrift::writeInt16(uint16_t value) -{ - value = htons(value); - m_buf.append(&value, sizeof(value)); -} - -void FbThrift::writeInt32(int32_t value) -{ - writeIntV((value << 1) ^ (value >> 31)); -} - -void FbThrift::writeInt64(int64_t value) -{ - writeIntV((value << 1) ^ (value >> 63)); -} - -void FbThrift::writeIntV(uint64_t value) -{ - bool bLast; - do { - bLast = (value & ~0x7F) == 0; - uint8_t b = value & 0x7F; - if (!bLast) { - b |= 0x80; - value >>= 7; - } - m_buf.append(&b, 1); - } while (!bLast); -} +// MqttMessage class members MqttMessage::MqttMessage() : m_leadingByte(0) @@ -197,6 +83,19 @@ MqttMessage::MqttMessage(FbMqttMessageType type, uint8_t flags) m_leadingByte = ((type & 0x0F) << 4) | (flags & 0x0F); } +char* MqttMessage::readStr(const uint8_t *&pData) const +{ + u_short len = ntohs(*(u_short *)pData); pData += sizeof(u_short); + if (len == 0) + return nullptr; + + char *res = (char*)mir_alloc(len + 1); + memcpy(res, pData, len); + res[len] = 0; + pData += len; + return res; +} + void MqttMessage::writeStr(const char *str) { size_t len = mir_strlen(str); @@ -224,7 +123,9 @@ bool FacebookProto::MqttConnect() bool FacebookProto::MqttParse(const MqttMessage &payload) { - auto *pData = (const uint8_t *)payload.data(); + auto *pData = (const uint8_t *)payload.data(), *pBeg = pData; + int flags = payload.getFlags(); + uint16_t mid; switch (payload.getType()) { case FB_MQTT_MESSAGE_TYPE_CONNACK: @@ -236,6 +137,30 @@ bool FacebookProto::MqttParse(const MqttMessage &payload) OnLoggedIn(); MqttPing(); break; + + case FB_MQTT_MESSAGE_TYPE_PUBREL: + mid = ntohs(*(u_short *)pData); + { + MqttMessage reply(FB_MQTT_MESSAGE_TYPE_PUBCOMP); + reply << mid; + MqttSend(reply); + } + break; + + case FB_MQTT_MESSAGE_TYPE_PUBLISH: + char *str = payload.readStr(pData); + + if ((flags & FB_MQTT_MESSAGE_FLAG_QOS1) || (flags & FB_MQTT_MESSAGE_FLAG_QOS2)) { + mid = ntohs(*(u_short *)pData); + + MqttMessage reply((flags & FB_MQTT_MESSAGE_FLAG_QOS1) ? FB_MQTT_MESSAGE_TYPE_PUBACK : FB_MQTT_MESSAGE_TYPE_PUBREC); + reply << mid; + MqttSend(reply); + } + + OnPublish(str, pData, payload.size() - (pData - pBeg)); + mir_free(str); + break; } return true; @@ -271,19 +196,6 @@ bool FacebookProto::MqttRead(MqttMessage &payload) payload.writeBuf(buf, res); remainingBytes -= res; } - - // that might be a zipped buffer - if (payload.size() >= 2) { - auto *p = (const uint8_t *)payload.data(); - if ((((p[0] << 8) | p[1]) % 31) == 0 && (p[0] & 0x0F) == 8) { // zip header ok - size_t dataSize; - void *pData = doUnzip(payload.size(), payload.data(), dataSize); - if (pData != nullptr) { - payload.reset(dataSize, pData); - mir_free(pData); - } - } - } } return true; diff --git a/protocols/Facebook/src/mqtt.h b/protocols/Facebook/src/mqtt.h index dc7fcd41fc..57df55a7a2 100644 --- a/protocols/Facebook/src/mqtt.h +++ b/protocols/Facebook/src/mqtt.h @@ -62,7 +62,7 @@ public: __forceinline void* data() const { return m_buf.data(); } __forceinline size_t size() const { return m_buf.length(); } - __forceinline void reset(const size_t cbLen, void *pData) + __forceinline void reset(size_t cbLen, void *pData) { m_buf.assign(pData, cbLen); } @@ -80,6 +80,26 @@ public: void writeList(int iType, int size); }; +class FbThriftReader : public FbThrift +{ + bool m_lastBool = false, m_lastBval = false; + size_t offset = 0; + + uint8_t decodeType(int type); + +public: + bool isStop(); + bool readBool(bool &bVal); + bool readByte(uint8_t &val); + bool readField(uint8_t &type, uint16_t &id); + bool readInt16(uint16_t &val); + bool readInt32(uint32_t &val); + bool readInt64(uint64_t &val); + bool readIntV(uint64_t &val); + bool readList(uint8_t &type, uint32_t &size); + bool readStr(char *&val); // val must be freed via mir_free() +}; + class MqttMessage : public FbThrift { friend class FacebookProto; @@ -99,6 +119,7 @@ public: { return m_leadingByte & 0x0F; } + char* readStr(const uint8_t *&pData) const; void writeStr(const char *str); }; @@ -110,3 +131,7 @@ public: #define FB_MQTT_CONNECT_FLAG_RET 0x0020 #define FB_MQTT_CONNECT_FLAG_PASS 0x0040 #define FB_MQTT_CONNECT_FLAG_USER 0x0080 + +#define FB_MQTT_MESSAGE_FLAG_QOS0 0x0000 +#define FB_MQTT_MESSAGE_FLAG_QOS1 0x0002 +#define FB_MQTT_MESSAGE_FLAG_QOS2 0x0004 diff --git a/protocols/Facebook/src/proto.h b/protocols/Facebook/src/proto.h index 01a0bae4f4..ad46f4c454 100644 --- a/protocols/Facebook/src/proto.h +++ b/protocols/Facebook/src/proto.h @@ -359,6 +359,9 @@ class FacebookProto : public PROTO<FacebookProto> bool MqttParse(const MqttMessage &payload); void MqttSend(const MqttMessage &payload); + void OnPublish(const char *str, const uint8_t *payLoad, size_t cbLen); + void OnPublishP(FbThriftReader &rdr); + HNETLIBCONN m_mqttConn; // internal data diff --git a/protocols/Facebook/src/server.cpp b/protocols/Facebook/src/server.cpp index a2753154e5..573f7a2c85 100644 --- a/protocols/Facebook/src/server.cpp +++ b/protocols/Facebook/src/server.cpp @@ -138,3 +138,85 @@ FAIL: m_iDesiredStatus = m_iStatus = ID_STATUS_OFFLINE; ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldStatus, m_iStatus); } + +///////////////////////////////////////////////////////////////////////////////////////// + +void FacebookProto::OnPublish(const char *topic, const uint8_t *p, size_t cbLen) +{ + FbThriftReader rdr; + + // that might be a zipped buffer + if (cbLen >= 2) { + if ((((p[0] << 8) | p[1]) % 31) == 0 && (p[0] & 0x0F) == 8) { // zip header ok + size_t dataSize; + void *pData = doUnzip(cbLen, p, dataSize); + if (pData != nullptr) { + rdr.reset(dataSize, pData); + mir_free(pData); + } + } + } + + if (rdr.size() == 0) + rdr.reset(cbLen, (void*)p); + + if (!strcmp(topic, "/t_p")) + OnPublishP(rdr); + +} + +void FacebookProto::OnPublishP(FbThriftReader &rdr) +{ + char *str; + assert(rdr.readStr(str)); + mir_free(str); + + bool bVal; + uint8_t fieldType; + uint16_t fieldId; + assert(rdr.readField(fieldType, fieldId)); + assert(fieldType == FB_THRIFT_TYPE_BOOL); + assert(fieldId == 1); + assert(rdr.readBool(bVal)); + + assert(rdr.readField(fieldType, fieldId)); + assert(fieldType == FB_THRIFT_TYPE_LIST); + assert(fieldId == 1); + + uint32_t size; + assert(rdr.readList(fieldType, size)); + assert(fieldType == FB_THRIFT_TYPE_STRUCT); + + for (uint32_t i = 0; i < size; i++) { + uint64_t userId, timestamp, voipBits; + assert(rdr.readField(fieldType, fieldId)); + assert(fieldType == FB_THRIFT_TYPE_I64); + assert(fieldId == 1); + assert(rdr.readInt64(userId)); + + uint32_t u32; + assert(rdr.readField(fieldType, fieldId)); + assert(fieldType == FB_THRIFT_TYPE_I32); + assert(fieldId == 1); + assert(rdr.readInt32(u32)); + + debugLogA("Presence from user %lld => %d", userId, u32); + + assert(rdr.readField(fieldType, fieldId)); + assert(fieldType == FB_THRIFT_TYPE_I64); + assert(fieldId == 1); + assert(rdr.readInt64(timestamp)); + + while (!rdr.isStop()) { + assert(rdr.readField(fieldType, fieldId)); + assert(fieldType == FB_THRIFT_TYPE_I64 || fieldType == FB_THRIFT_TYPE_I16 || fieldType == FB_THRIFT_TYPE_I32); + assert(rdr.readIntV(voipBits)); + } + + assert(rdr.readByte(fieldType)); + assert(fieldType == FB_THRIFT_TYPE_STOP); + } + + assert(rdr.readByte(fieldType)); + assert(fieldType == FB_THRIFT_TYPE_STOP); +} diff --git a/protocols/Facebook/src/stdafx.h b/protocols/Facebook/src/stdafx.h index 95eb7d76a5..f24195c746 100644 --- a/protocols/Facebook/src/stdafx.h +++ b/protocols/Facebook/src/stdafx.h @@ -25,6 +25,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include <stdio.h> #include <stdarg.h> #include <string.h> +#include <assert.h> #include <win2k.h> #include <newpluginapi.h> diff --git a/protocols/Facebook/src/thrift.cpp b/protocols/Facebook/src/thrift.cpp new file mode 100644 index 0000000000..aefc7143a9 --- /dev/null +++ b/protocols/Facebook/src/thrift.cpp @@ -0,0 +1,306 @@ +/* + +Facebook plugin for Miranda NG +Copyright © 2019 Miranda NG team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. + +*/ + +///////////////////////////////////////////////////////////////////////////////////////// +// FbThrift class members + +static uint8_t encodeType(int type) +{ + switch (type) { + case FB_THRIFT_TYPE_BOOL: + return 2; + case FB_THRIFT_TYPE_BYTE: + return 3; + case FB_THRIFT_TYPE_I16: + return 4; + case FB_THRIFT_TYPE_I32: + return 5; + case FB_THRIFT_TYPE_I64: + return 6; + case FB_THRIFT_TYPE_DOUBLE: + return 7; + case FB_THRIFT_TYPE_STRING: + return 8; + case FB_THRIFT_TYPE_LIST: + return 9; + case FB_THRIFT_TYPE_SET: + return 10; + case FB_THRIFT_TYPE_MAP: + return 11; + case FB_THRIFT_TYPE_STRUCT: + return 12; + default: + return 0; + } +} + +FbThrift& FbThrift::operator<<(uint8_t value) +{ + m_buf.append(&value, 1); + return *this; +} + +FbThrift& FbThrift::operator<<(const char *str) +{ + size_t len = mir_strlen(str); + writeIntV(len); + m_buf.append((void*)str, len); + return *this; +} + +void FbThrift::writeBool(bool bValue) +{ + uint8_t b = (bValue) ? 0x11 : 0x12; + m_buf.append(&b, 1); +} + +void FbThrift::writeBuf(const void *pData, size_t cbLen) +{ + m_buf.append((void*)pData, cbLen); +} + +void FbThrift::writeField(int iType) +{ + uint8_t type = encodeType(iType) + 0x10; + m_buf.append(&type, 1); +} + +void FbThrift::writeField(int iType, int id, int lastid) +{ + uint8_t type = encodeType(iType); + uint8_t diff = uint8_t(id - lastid); + if (diff > 0x0F) { + m_buf.append(&type, 1); + writeInt64(id); + } + else { + type += (diff << 4); + m_buf.append(&type, 1); + } +} + +void FbThrift::writeList(int iType, int size) +{ + uint8_t type = encodeType(iType); + if (size > 14) { + writeIntV(size); + *this << (type | 0xF0); + } + else *this << (type | (size << 4)); +} + +void FbThrift::writeInt16(uint16_t value) +{ + value = htons(value); + m_buf.append(&value, sizeof(value)); +} + +void FbThrift::writeInt32(int32_t value) +{ + writeIntV((value << 1) ^ (value >> 31)); +} + +void FbThrift::writeInt64(int64_t value) +{ + writeIntV((value << 1) ^ (value >> 63)); +} + +void FbThrift::writeIntV(uint64_t value) +{ + bool bLast; + do { + bLast = (value & ~0x7F) == 0; + uint8_t b = value & 0x7F; + if (!bLast) { + b |= 0x80; + value >>= 7; + } + m_buf.append(&b, 1); + } while (!bLast); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// FbThriftReader class members + +uint8_t FbThriftReader::decodeType(int type) +{ + switch (type) { + case 0: + return FB_THRIFT_TYPE_STOP; + case 1: + m_lastBval = m_lastBool = true; + return FB_THRIFT_TYPE_BOOL; + case 2: + m_lastBool = true; + m_lastBval = false; + return FB_THRIFT_TYPE_BOOL; + case 3: + return FB_THRIFT_TYPE_BYTE; + case 4: + return FB_THRIFT_TYPE_I16; + case 5: + return FB_THRIFT_TYPE_I32; + case 6: + return FB_THRIFT_TYPE_I64; + case 7: + return FB_THRIFT_TYPE_DOUBLE; + case 8: + return FB_THRIFT_TYPE_STRING; + case 9: + return FB_THRIFT_TYPE_LIST; + case 10: + return FB_THRIFT_TYPE_SET; + case 11: + return FB_THRIFT_TYPE_MAP; + case 12: + return FB_THRIFT_TYPE_STRUCT; + default: + return 0; + } +} + +bool FbThriftReader::isStop() +{ + byte b; + if (!readByte(b)) + return false; + + offset--; + return b == FB_THRIFT_TYPE_STOP; +} + +bool FbThriftReader::readBool(bool &bVal) +{ + if (m_lastBool) { + bVal = m_lastBval; + m_lastBool = false; + return true; + } + + byte b; + if (!readByte(b)) + return false; + + bVal = b == 0x11; + return true; +} + +bool FbThriftReader::readByte(uint8_t &b) +{ + if (offset >= size()) + return false; + + b = *((uint8_t *)data() + offset); + offset++; + return true; +} + +bool FbThriftReader::readField(uint8_t &type, uint16_t &id) +{ + byte b; + if (!readByte(b)) + return false; + + type = decodeType(b & 0x0F); + id = (b >> 4); + return (id == 0) ? readInt16(id) : true; +} + +bool FbThriftReader::readIntV(uint64_t &val) +{ + uint8_t b; + unsigned i = 0; + val = 0; + + do { + if (!readByte(b)) + return false; + + val |= ((b & 0x7F) << i); + i += 7; + } while ((b & 0x80) != 0); + + return true; +} + +bool FbThriftReader::readList(uint8_t &type, uint32_t &size) +{ + byte b; + if (!readByte(b)) + return false; + + type = decodeType(b & 0x0F); + size = b >> 4; + if (size == 0x0F) { + uint64_t tmp; + if (!readIntV(tmp)) + return false; + size = (uint32_t)tmp; + } + return true; +} + +bool FbThriftReader::readStr(char *&val) +{ + uint64_t tmp; + if (!readIntV(tmp)) + return false; + + uint32_t cbLen = (uint32_t)tmp; + if (offset + cbLen >= size()) + return false; + + if (cbLen > 0) { + val = mir_strndup((char *)data() + offset, cbLen); + offset += cbLen; + } + else val = nullptr; + return true; +} + +bool FbThriftReader::readInt16(uint16_t &val) +{ + if (offset + 2 >= size()) + return false; + + val = ntohs(*(u_short *)((char *)data() + offset)); + offset += 2; + return true; +} + +bool FbThriftReader::readInt32(uint32_t &val) +{ + uint64_t tmp; + if (!readIntV(tmp)) + return false; + + val = (uint32_t )tmp; + return true; +} + +bool FbThriftReader::readInt64(uint64_t &val) +{ + uint64_t tmp; + if (!readIntV(tmp)) + return false; + + val = (tmp >> 0x01) ^ -(tmp & 0x01); + return true; +} |