📄 network-mysqld-proto.c
字号:
/* Copyright (C) 2007 MySQL AB 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; version 2 of the License. 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <string.h>#include "network-mysqld-proto.h"#include "sys-pedantic.h"/** @file * * decoders and encoders for the MySQL packets * * - basic data-types * - fixed length integers * - variable length integers * - variable length strings * - packet types * - OK packets * - EOF packets * - ERR packets * *//** * force a crash for gdb and valgrind to get a stacktrace */#define CRASHME() do { char *_crashme = NULL; *_crashme = 0; } while(0);/** * a handy marco for constant strings */#define C(x) x, sizeof(x) - 1/** @defgroup proto MySQL Protocol * * decoders and encoders for the MySQL packets as described in * http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol * * *//*@{*//** * decode a length-encoded integer from a network packet * * _off is incremented on success * * @param packet the MySQL-packet to decode * @param _off offset in into the packet * @return the decoded number * */guint64 network_mysqld_proto_get_lenenc_int(GString *packet, guint *_off) { guint off = *_off; guint64 ret = 0; unsigned char *bytestream = (unsigned char *)packet->str; g_assert(off < packet->len); if (bytestream[off] < 251) { /* */ ret = bytestream[off]; } else if (bytestream[off] == 251) { /* NULL in row-data */ ret = bytestream[off]; } else if (bytestream[off] == 252) { /* 2 byte length*/ g_assert(off + 2 < packet->len); ret = (bytestream[off + 1] << 0) | (bytestream[off + 2] << 8) ; off += 2; } else if (bytestream[off] == 253) { /* 3 byte */ g_assert(off + 3 < packet->len); ret = (bytestream[off + 1] << 0) | (bytestream[off + 2] << 8) | (bytestream[off + 3] << 16); off += 3; } else if (bytestream[off] == 254) { /* 8 byte */ g_assert(off + 8 < packet->len); ret = (bytestream[off + 5] << 0) | (bytestream[off + 6] << 8) | (bytestream[off + 7] << 16) | (bytestream[off + 8] << 24); ret <<= 32; ret |= (bytestream[off + 1] << 0) | (bytestream[off + 2] << 8) | (bytestream[off + 3] << 16) | (bytestream[off + 4] << 24); off += 8; } else { g_error("%s.%d: bytestream[%d] is %d", __FILE__, __LINE__, off, bytestream[off]); } off += 1; *_off = off; return ret;}/** * decode a OK packet from the network packet */int network_mysqld_proto_get_ok_packet(GString *packet, guint64 *affected, guint64 *insert_id, int *server_status, int *warning_count, char **msg) { guint off = 0; guint64 dest; guint field_count; field_count = network_mysqld_proto_get_int8(packet, &off); g_assert(field_count == 0); dest = network_mysqld_proto_get_lenenc_int(packet, &off); if (affected) *affected = dest; dest = network_mysqld_proto_get_lenenc_int(packet, &off); if (insert_id) *insert_id = dest; dest = network_mysqld_proto_get_int16(packet, &off); if (server_status) *server_status = dest; dest = network_mysqld_proto_get_int16(packet, &off); if (warning_count) *warning_count = dest; if (msg) *msg = NULL; return 0;}int network_mysqld_proto_append_ok_packet(GString *packet, guint64 affected_rows, guint64 insert_id, guint16 server_status, guint16 warnings) { network_mysqld_proto_append_int8(packet, 0); /* no fields */ network_mysqld_proto_append_lenenc_int(packet, affected_rows); network_mysqld_proto_append_lenenc_int(packet, insert_id); network_mysqld_proto_append_int16(packet, server_status); /* autocommit */ network_mysqld_proto_append_int16(packet, warnings); /* no warnings */ return 0;}/** * create a ERR packet * * @note the sqlstate has to match the SQL standard. If no matching SQL state is known, leave it at NULL * * @param packet network packet * @param errmsg the error message * @param errmsg_len byte-len of the error-message * @param errorcode mysql error-code we want to send * @param sqlstate if none-NULL, 5-char SQL state to send, if NULL, default SQL state is used * * @return 0 on success */int network_mysqld_proto_append_error_packet(GString *packet, const char *errmsg, gsize errmsg_len, guint errorcode, const gchar *sqlstate) { network_mysqld_proto_append_int8(packet, 0xff); /* ERR */ network_mysqld_proto_append_int16(packet, errorcode); /* errorcode */ g_string_append_c(packet, '#'); if (!sqlstate) { g_string_append_len(packet, C("07000")); } else { g_string_append_len(packet, sqlstate, 5); } if (errmsg_len < 512) { g_string_append_len(packet, errmsg, errmsg_len); } else { /* truncate the err-msg */ g_string_append_len(packet, errmsg, 512); } return 0;}/** * skip bytes in the network packet * * a assertion makes sure that we can't skip over the end of the packet * * @param packet the MySQL network packet * @param _off offset into the packet * @param size bytes to skip * */void network_mysqld_proto_skip(GString *packet, guint *_off, gsize size) { g_assert(*_off + size <= packet->len); *_off += size;}/** * get a fixed-length integer from the network packet * * @param packet the MySQL network packet * @param _off offset into the packet * @param size byte-len of the integer to decode * @return a the decoded integer */guint64 network_mysqld_proto_get_int_len(GString *packet, guint *_off, gsize size) { gsize i; int shift; guint64 r = 0; guint off = *_off; g_assert(*_off < packet->len); if (*_off + size > packet->len) { CRASHME(); } g_assert(*_off + size <= packet->len); for (i = 0, shift = 0; i < size; i++, shift += 8) { r += (unsigned char)(packet->str[off + i]) << shift; } *_off += size; return r;}/** * get a 8-bit integer from the network packet * * @param packet the MySQL network packet * @param _off offset into the packet * @return a the decoded integer * @see network_mysqld_proto_get_int_len() */guint8 network_mysqld_proto_get_int8(GString *packet, guint *_off) { return network_mysqld_proto_get_int_len(packet, _off, 1);}/** * get a 16-bit integer from the network packet * * @param packet the MySQL network packet * @param _off offset into the packet * @return a the decoded integer * @see network_mysqld_proto_get_int_len() */guint16 network_mysqld_proto_get_int16(GString *packet, guint *_off) { return network_mysqld_proto_get_int_len(packet, _off, 2);}/** * get a 32-bit integer from the network packet * * @param packet the MySQL network packet * @param _off offset into the packet * @return a the decoded integer * @see network_mysqld_proto_get_int_len() */guint32 network_mysqld_proto_get_int32(GString *packet, guint *_off) { return network_mysqld_proto_get_int_len(packet, _off, 4);}/** * get a string from the network packet * * @param packet the MySQL network packet * @param _off offset into the packet * @param len length of the string * @return the string (allocated) or NULL of len is 0 */gchar *network_mysqld_proto_get_string_len(GString *packet, guint *_off, gsize len) { gchar *str; g_assert(*_off < packet->len); if (*_off + len > packet->len) { g_error("packet-offset out of range: %u + "F_SIZE_T" > "F_SIZE_T, *_off, len, packet->len); } str = len ? g_strndup(packet->str + *_off, len) : NULL; *_off += len; return str;}/** * get a variable-length string from the network packet * * variable length strings are prefixed with variable-length integer defining the length of the string * * @param packet the MySQL network packet * @param _off offset into the packet * @return the string * @see network_mysqld_proto_get_string_len(), network_mysqld_proto_get_lenenc_int() */gchar *network_mysqld_proto_get_lenenc_string(GString *packet, guint *_off) { guint64 len; len = network_mysqld_proto_get_lenenc_int(packet, _off); g_assert(*_off < packet->len); g_assert(*_off + len <= packet->len); return network_mysqld_proto_get_string_len(packet, _off, len);}/** * get a NUL-terminated string from the network packet * * @param packet the MySQL network packet * @param _off offset into the packet * @return the string * @see network_mysqld_proto_get_string_len() */gchar *network_mysqld_proto_get_string(GString *packet, guint *_off) { guint len; gchar *r = NULL; for (len = 0; *_off + len < packet->len && *(packet->str + *_off + len); len++);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -