diff --git a/include/MySQLProtocolUtils.h b/include/MySQLProtocolUtils.h new file mode 100644 index 000000000..83c5ccf4f --- /dev/null +++ b/include/MySQLProtocolUtils.h @@ -0,0 +1,51 @@ +/** + * @file MySQLProtocolUtils.h + * @brief Pure MySQL protocol utility functions for unit testability. + * + * Extracted from MySQLFFTO for testing. These are low-level protocol + * parsing helpers that operate on raw byte buffers. + * + * @see FFTO unit testing (GitHub issue #5499) + */ + +#ifndef MYSQL_PROTOCOL_UTILS_H +#define MYSQL_PROTOCOL_UTILS_H + +#include +#include + +/** + * @brief Read a MySQL length-encoded integer from a buffer. + * + * MySQL length-encoded integers use 1-9 bytes: + * - 0x00-0xFA: 1 byte (value itself) + * - 0xFC: 2 bytes follow (uint16) + * - 0xFD: 3 bytes follow (uint24) + * - 0xFE: 8 bytes follow (uint64) + * + * @param buf [in/out] Pointer to current position; advanced past the integer. + * @param len [in/out] Remaining buffer length; decremented. + * @return Decoded 64-bit integer value (0 on error/truncation). + */ +uint64_t mysql_read_lenenc_int(const unsigned char* &buf, size_t &len); + +/** + * @brief Build a MySQL protocol packet header + payload. + * + * Constructs a complete MySQL wire-format packet: 3-byte length + + * 1-byte sequence number + payload. + * + * @param payload Payload data. + * @param payload_len Length of payload. + * @param seq_id Packet sequence number. + * @param out_buf Output buffer (must be at least payload_len + 4). + * @return Total packet size (payload_len + 4). + */ +size_t mysql_build_packet( + const unsigned char *payload, + uint32_t payload_len, + uint8_t seq_id, + unsigned char *out_buf +); + +#endif // MYSQL_PROTOCOL_UTILS_H diff --git a/lib/Makefile b/lib/Makefile index cc5449907..21c07a339 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -109,6 +109,7 @@ _OBJ_CXX := ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo TransactionState.oo \ HostgroupRouting.oo \ PgSQLCommandComplete.oo \ + MySQLProtocolUtils.oo \ proxy_sqlite3_symbols.oo # TSDB object files diff --git a/lib/MySQLProtocolUtils.cpp b/lib/MySQLProtocolUtils.cpp new file mode 100644 index 000000000..562ac1619 --- /dev/null +++ b/lib/MySQLProtocolUtils.cpp @@ -0,0 +1,59 @@ +/** + * @file MySQLProtocolUtils.cpp + * @brief Implementation of MySQL protocol utility functions. + * + * @see MySQLProtocolUtils.h + */ + +#include "MySQLProtocolUtils.h" +#include + +uint64_t mysql_read_lenenc_int(const unsigned char* &buf, size_t &len) { + if (len == 0) return 0; + uint8_t first_byte = buf[0]; + buf++; len--; + if (first_byte < 0xFB) return first_byte; + if (first_byte == 0xFC) { + if (len < 2) return 0; + uint64_t value = buf[0] | (static_cast(buf[1]) << 8); + buf += 2; len -= 2; + return value; + } + if (first_byte == 0xFD) { + if (len < 3) return 0; + uint64_t value = buf[0] | (static_cast(buf[1]) << 8) + | (static_cast(buf[2]) << 16); + buf += 3; len -= 3; + return value; + } + if (first_byte == 0xFE) { + if (len < 8) return 0; + uint64_t value = buf[0] | (static_cast(buf[1]) << 8) + | (static_cast(buf[2]) << 16) + | (static_cast(buf[3]) << 24) + | (static_cast(buf[4]) << 32) + | (static_cast(buf[5]) << 40) + | (static_cast(buf[6]) << 48) + | (static_cast(buf[7]) << 56); + buf += 8; len -= 8; + return value; + } + return 0; +} + +size_t mysql_build_packet( + const unsigned char *payload, + uint32_t payload_len, + uint8_t seq_id, + unsigned char *out_buf) +{ + // 3-byte length (little-endian) + 1-byte sequence + out_buf[0] = payload_len & 0xFF; + out_buf[1] = (payload_len >> 8) & 0xFF; + out_buf[2] = (payload_len >> 16) & 0xFF; + out_buf[3] = seq_id; + if (payload && payload_len > 0) { + memcpy(out_buf + 4, payload, payload_len); + } + return payload_len + 4; +}