summaryrefslogtreecommitdiff
path: root/ext/mysqlnd/mysqlnd_wireprotocol.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/mysqlnd/mysqlnd_wireprotocol.c')
-rw-r--r--ext/mysqlnd/mysqlnd_wireprotocol.c357
1 files changed, 8 insertions, 349 deletions
diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c
index cb870be025..de8143a04b 100644
--- a/ext/mysqlnd/mysqlnd_wireprotocol.c
+++ b/ext/mysqlnd/mysqlnd_wireprotocol.c
@@ -27,15 +27,11 @@
#include "mysqlnd_block_alloc.h"
#include "ext/standard/sha1.h"
#include "zend_ini.h"
-#ifdef MYSQLND_COMPRESSION_ENABLED
-#include <zlib.h>
-#endif
#define MYSQLND_SILENT 1
#define MYSQLND_DUMP_HEADER_N_BODY
-
#define PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_size, packet_type_as_text, packet_type) \
{ \
DBG_INF_FMT("buf=%p size=%u", (buf), (buf_size)); \
@@ -51,7 +47,7 @@
(buf_size), (packet)->header.size, (packet)->header.size - (buf_size)); \
DBG_RETURN(FAIL); \
}\
- if (FAIL == mysqlnd_read_body((conn), &((packet)->header), (buf) TSRMLS_CC)) { \
+ if (FAIL == conn->net->m.receive((conn), (buf), (packet)->header.size TSRMLS_CC)) { \
CONN_SET_STATE(conn, CONN_QUIT_SENT); \
SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \
@@ -205,72 +201,6 @@ zend_uchar *php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length)
/* }}} */
-#ifdef MYSQLND_COMPRESSION_ENABLED
-/* {{{ php_mysqlnd_read_buffer_is_empty */
-static zend_bool
-php_mysqlnd_read_buffer_is_empty(MYSQLND_READ_BUFFER * buffer)
-{
- return buffer->len? FALSE:TRUE;
-}
-/* }}} */
-
-
-/* {{{ php_mysqlnd_read_buffer_read */
-static void
-php_mysqlnd_read_buffer_read(MYSQLND_READ_BUFFER * buffer, size_t count, zend_uchar * dest)
-{
- if (buffer->len >= count) {
- memcpy(dest, buffer->data + buffer->offset, count);
- buffer->offset += count;
- buffer->len -= count;
- }
-}
-/* }}} */
-
-
-/* {{{ php_mysqlnd_read_buffer_bytes_left */
-static size_t
-php_mysqlnd_read_buffer_bytes_left(MYSQLND_READ_BUFFER * buffer)
-{
- return buffer->len;
-}
-/* }}} */
-
-
-/* {{{ php_mysqlnd_read_buffer_free */
-static void
-php_mysqlnd_read_buffer_free(MYSQLND_READ_BUFFER ** buffer TSRMLS_DC)
-{
- DBG_ENTER("php_mysqlnd_read_buffer_free");
- if (*buffer) {
- mnd_efree((*buffer)->data);
- mnd_efree(*buffer);
- *buffer = NULL;
- }
- DBG_VOID_RETURN;
-}
-/* }}} */
-
-
-/* {{{ php_mysqlnd_create_read_buffer */
-static MYSQLND_READ_BUFFER *
-mysqlnd_create_read_buffer(size_t count TSRMLS_DC)
-{
- MYSQLND_READ_BUFFER * ret = mnd_emalloc(sizeof(MYSQLND_READ_BUFFER));
- DBG_ENTER("mysqlnd_create_read_buffer");
- ret->is_empty = php_mysqlnd_read_buffer_is_empty;
- ret->read = php_mysqlnd_read_buffer_read;
- ret->bytes_left = php_mysqlnd_read_buffer_bytes_left;
- ret->free_buffer = php_mysqlnd_read_buffer_free;
- ret->data = mnd_emalloc(count);
- ret->size = ret->len = count;
- ret->offset = 0;
- DBG_RETURN(ret);
-}
-/* }}} */
-#endif
-
-
/* {{{ php_mysqlnd_consume_uneaten_data */
#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
size_t php_mysqlnd_consume_uneaten_data(MYSQLND * const conn, enum php_mysqlnd_server_command cmd TSRMLS_DC)
@@ -353,268 +283,6 @@ php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_len,
/* }}} */
-/* We assume that MYSQLND_HEADER_SIZE is 4 bytes !! */
-#define STORE_HEADER_SIZE(safe_storage, buffer) int4store((safe_storage), (*(uint32_t *)(buffer)))
-#define RESTORE_HEADER_SIZE(buffer, safe_storage) STORE_HEADER_SIZE((safe_storage), (buffer))
-
-/* {{{ mysqlnd_stream_write_w_header */
-/*
- IMPORTANT : It's expected that buf has place in the beginning for MYSQLND_HEADER_SIZE !!!!
- This is done for performance reasons in the caller of this function.
- Otherwise we will have to do send two TCP packets, or do new alloc and memcpy.
- Neither are quick, thus the clients of this function are obligated to do
- what they are asked for.
-
- `count` is actually the length of the payload data. Thus :
- count + MYSQLND_HEADER_SIZE = sizeof(buf) (not the pointer but the actual buffer)
-*/
-size_t mysqlnd_stream_write_w_header(MYSQLND * const conn, char * const buf, size_t count TSRMLS_DC)
-{
- zend_uchar safe_buf[((MYSQLND_HEADER_SIZE) + (sizeof(zend_uchar)) - 1) / (sizeof(zend_uchar))];
- zend_uchar *safe_storage = safe_buf;
- MYSQLND_NET *net = conn->net;
- size_t old_chunk_size = net->stream->chunk_size;
- size_t ret, packets_sent = 1;
- size_t left = count;
- zend_uchar *p = (zend_uchar *) buf;
- zend_uchar * compress_buf = NULL;
- size_t to_be_sent;
-
- DBG_ENTER("mysqlnd_stream_write_w_header");
- DBG_INF_FMT("conn=%llu count=%lu compression=%d", conn->thread_id, count, net->compressed);
-
- net->stream->chunk_size = MYSQLND_MAX_PACKET_SIZE;
-
- if (net->compressed == TRUE) {
- size_t comp_buf_size = MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE + MIN(left, MYSQLND_MAX_PACKET_SIZE);
- DBG_INF_FMT("compress_buf_size=%d", comp_buf_size);
- compress_buf = emalloc(comp_buf_size);
- }
-
- do {
- to_be_sent = MIN(left, MYSQLND_MAX_PACKET_SIZE);
-#ifdef MYSQLND_COMPRESSION_ENABLED
- if (net->compressed == TRUE) {
- /* here we need to compress the data and then write it, first comes the compressed header */
- uLong tmp_complen = to_be_sent;
- size_t payload_size;
- zend_uchar * uncompressed_payload = p; /* should include the header */
- int res;
-
- STORE_HEADER_SIZE(safe_storage, uncompressed_payload);
- int3store(uncompressed_payload, to_be_sent);
- int1store(uncompressed_payload + 3, net->packet_no);
-
- DBG_INF_FMT("compress(%p, %p, %p, %d)", (compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen, p, to_be_sent + MYSQLND_HEADER_SIZE);
- res = compress((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen, uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE);
- if (res == Z_OK) {
- DBG_INF_FMT("compression successful. compressed size=%d", tmp_complen);
- int3store(compress_buf + MYSQLND_HEADER_SIZE, to_be_sent + MYSQLND_HEADER_SIZE);
- payload_size = tmp_complen;
- } else {
- DBG_INF_FMT("compression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", res, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR);
- int3store(compress_buf + MYSQLND_HEADER_SIZE, 0);
- memcpy(compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE);
- payload_size = to_be_sent + MYSQLND_HEADER_SIZE;
- }
- RESTORE_HEADER_SIZE(uncompressed_payload, safe_storage);
-
- int3store(compress_buf, payload_size);
- int1store(compress_buf + 3, net->packet_no);
- DBG_INF_FMT("writing %d bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE);
- ret = conn->net->m.stream_write(conn, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE TSRMLS_CC);
- net->compressed_envelope_packet_no++;
- #if WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY
- if (res == Z_OK) {
- size_t decompressed_size = left + MYSQLND_HEADER_SIZE;
- uLongf tmp_complen2 = decompressed_size;
- zend_uchar * decompressed_data = malloc(decompressed_size);
- int error = uncompress(decompressed_data, &tmp_complen2, compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, payload_size);
- if (error == Z_OK) {
- int i;
- DBG_INF("success decompressing");
- for (i = 0 ; i < decompressed_size; i++) {
- if (i && (i % 30 == 0)) {
- printf("\n\t\t");
- }
- printf("%.2X ", (int)*((char*)&(decompressed_data[i])));
- DBG_INF_FMT("%.2X ", (int)*((char*)&(decompressed_data[i])));
- }
- } else {
- DBG_INF("error decompressing");
- }
- free(decompressed_data);
- }
- #endif /* WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY */
- } else
-#endif /* MYSQLND_COMPRESSION_ENABLED */
- {
- DBG_INF("no compression");
- STORE_HEADER_SIZE(safe_storage, p);
- int3store(p, to_be_sent);
- int1store(p + 3, net->packet_no);
- ret = conn->net->m.stream_write(conn, p, to_be_sent + MYSQLND_HEADER_SIZE TSRMLS_CC);
- RESTORE_HEADER_SIZE(p, safe_storage);
- net->compressed_envelope_packet_no++;
- }
- net->packet_no++;
-
- p += to_be_sent;
- left -= to_be_sent;
- packets_sent++;
- /*
- if left is 0 then there is nothing more to send, but if the last packet was exactly
- with the size MYSQLND_MAX_PACKET_SIZE we need to send additional packet, which has
- empty payload. Thus if left == 0 we check for to_be_sent being the max size. If it is
- indeed it then loop once more, then to_be_sent will become 0, left will stay 0. Empty
- packet will be sent and this loop will end.
- */
- } while (ret && (left > 0 || to_be_sent == MYSQLND_MAX_PACKET_SIZE));
-
- DBG_INF_FMT("packet_size=%d packet_no=%d", left, net->packet_no);
- /* Even for zero size payload we have to send a packet */
- if (!ret) {
- DBG_ERR_FMT("Can't %u send bytes", count);
- conn->state = CONN_QUIT_SENT;
- SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
- }
-
- MYSQLND_INC_CONN_STATISTIC_W_VALUE3(&conn->stats,
- STAT_BYTES_SENT, count + packets_sent * MYSQLND_HEADER_SIZE,
- STAT_PROTOCOL_OVERHEAD_OUT, packets_sent * MYSQLND_HEADER_SIZE,
- STAT_PACKETS_SENT, packets_sent);
-
- net->stream->chunk_size = old_chunk_size;
- if (compress_buf) {
- efree(compress_buf);
- }
- DBG_RETURN(ret);
-}
-/* }}} */
-
-
-#ifdef MYSQLND_COMPRESSION_ENABLED
-/* {{{ mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer */
-static enum_func_status
-mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer(MYSQLND * conn, size_t net_payload_size TSRMLS_DC)
-{
- MYSQLND_NET * net = conn->net;
- size_t decompressed_size;
- enum_func_status ret = PASS;
- zend_uchar * compressed_data = NULL;
- zend_uchar comp_header[COMPRESSED_HEADER_SIZE];
- DBG_ENTER("mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer");
-
- /* Read the compressed header */
- if (FAIL == conn->net->m.stream_read(conn, comp_header, COMPRESSED_HEADER_SIZE TSRMLS_CC)) {
- DBG_RETURN(FAIL);
- }
- decompressed_size = uint3korr(comp_header);
-
- /* When decompressed_size is 0, then the data is not compressed, and we have wasted 3 bytes */
- /* we need to decompress the data */
-
- if (decompressed_size) {
- int error;
- uLongf tmp_complen = decompressed_size;
- compressed_data = emalloc(net_payload_size);
- if (FAIL == conn->net->m.stream_read(conn, compressed_data, net_payload_size TSRMLS_CC)) {
- ret = FAIL;
- goto end;
- }
-
- net->uncompressed_data = mysqlnd_create_read_buffer(decompressed_size TSRMLS_CC);
- error = uncompress(net->uncompressed_data->data, &tmp_complen, compressed_data, net_payload_size);
-
- DBG_INF_FMT("compressed data: decomp_len=%d compressed_size=%d", decompressed_size, net_payload_size);
- if (error != Z_OK) {
- DBG_ERR_FMT("Can't uncompress packet, error: %d", error);
- ret = FAIL;
- goto end;
- }
- } else {
- DBG_INF_FMT("The server decided not to compress the data. Our job is easy. Copying %u bytes", net_payload_size);
- net->uncompressed_data = mysqlnd_create_read_buffer(net_payload_size TSRMLS_CC);
- if (FAIL == conn->net->m.stream_read(conn, net->uncompressed_data->data, net_payload_size TSRMLS_CC)) {
- ret = FAIL;
- goto end;
- }
- }
-end:
- if (compressed_data) {
- efree(compressed_data);
- }
- DBG_RETURN(ret);
-}
-#endif /* MYSQLND_COMPRESSION_ENABLED */
-
-
-/* {{{ mysqlnd_real_read */
-static enum_func_status
-mysqlnd_real_read(MYSQLND * conn, zend_uchar * buffer, size_t count TSRMLS_DC)
-{
- size_t to_read = count;
- zend_uchar * p = buffer;
- MYSQLND_NET * net = conn->net;
-
- DBG_ENTER("mysqlnd_real_read");
-#ifdef MYSQLND_COMPRESSION_ENABLED
- if (net->compressed) {
- if (net->uncompressed_data) {
- size_t to_read_from_buffer = MIN(net->uncompressed_data->bytes_left(net->uncompressed_data), to_read);
- DBG_INF_FMT("reading %u from uncompressed_data buffer", to_read_from_buffer);
- if (to_read_from_buffer) {
- net->uncompressed_data->read(net->uncompressed_data, to_read_from_buffer, (zend_uchar *) p);
- p += to_read_from_buffer;
- to_read -= to_read_from_buffer;
- }
- DBG_INF_FMT("left %u to read", to_read);
- if (TRUE == net->uncompressed_data->is_empty(net->uncompressed_data)) {
- /* Everything was consumed. This should never happen here, but for security */
- net->uncompressed_data->free_buffer(&net->uncompressed_data TSRMLS_CC);
- }
- }
- if (to_read) {
- zend_uchar net_header[MYSQLND_HEADER_SIZE];
- size_t net_payload_size;
- zend_uchar packet_no;
-
- if (FAIL == net->m.stream_read(conn, net_header, MYSQLND_HEADER_SIZE TSRMLS_CC)) {
- DBG_RETURN(FAIL);
- }
- net_payload_size = uint3korr(net_header);
- packet_no = uint1korr(net_header + 3);
- if (net->compressed_envelope_packet_no != packet_no) {
- DBG_ERR_FMT("Transport level: packets out of order. Expected %d received %d. Packet size=%d",
- net->compressed_envelope_packet_no, packet_no, net_payload_size);
-
- php_error(E_WARNING, "Packets out of order. Expected %d received %d. Packet size="MYSQLND_SZ_T_SPEC,
- net->compressed_envelope_packet_no, packet_no, net_payload_size);
- DBG_RETURN(FAIL);
- }
- net->compressed_envelope_packet_no++;
-#ifdef MYSQLND_DUMP_HEADER_N_BODY
- DBG_INF_FMT("HEADER: hwd_packet_no=%d size=%3d", packet_no, net_payload_size);
-#endif
- /* Now let's read from the wire, decompress it and fill the read buffer */
- mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer(conn, net_payload_size TSRMLS_CC);
-
- /*
- Now a bit of recursion - read from the read buffer,
- if the data which we have just read from the wire
- is not enough, then the recursive call will try to
- satisfy it until it is satisfied.
- */
- DBG_RETURN(mysqlnd_real_read(conn, p, to_read TSRMLS_CC));
- }
- DBG_RETURN(PASS);
- }
-#endif /* MYSQLND_COMPRESSION_ENABLED */
- DBG_RETURN(net->m.stream_read(conn, p, to_read TSRMLS_CC));
-}
-/* }}} */
-
-
/* {{{ mysqlnd_read_header */
static enum_func_status
mysqlnd_read_header(MYSQLND * conn, mysqlnd_packet_header * header TSRMLS_DC)
@@ -624,7 +292,7 @@ mysqlnd_read_header(MYSQLND * conn, mysqlnd_packet_header * header TSRMLS_DC)
DBG_ENTER("mysqlnd_read_header_name");
DBG_INF_FMT("compressed=%d conn_id=%u", net->compressed, conn->thread_id);
- if (FAIL == mysqlnd_real_read(conn, buffer, MYSQLND_HEADER_SIZE TSRMLS_CC)) {
+ if (FAIL == net->m.receive(conn, buffer, MYSQLND_HEADER_SIZE TSRMLS_CC)) {
DBG_RETURN(FAIL);
}
@@ -658,16 +326,6 @@ mysqlnd_read_header(MYSQLND * conn, mysqlnd_packet_header * header TSRMLS_DC)
/* }}} */
-/* {{{ mysqlnd_read_body */
-static enum_func_status
-mysqlnd_read_body(MYSQLND *conn, mysqlnd_packet_header * header, zend_uchar * store_buf TSRMLS_DC)
-{
- DBG_ENTER(mysqlnd_read_body_name);
- DBG_RETURN(mysqlnd_real_read(conn, store_buf, header->size TSRMLS_CC));
-}
-/* }}} */
-
-
/* {{{ php_mysqlnd_greet_read */
static enum_func_status
php_mysqlnd_greet_read(void *_packet, MYSQLND *conn TSRMLS_DC)
@@ -872,7 +530,7 @@ size_t php_mysqlnd_auth_write(void *_packet, MYSQLND *conn TSRMLS_DC)
/* Handle CLIENT_CONNECT_WITH_DB */
/* no \0 for no DB */
- DBG_RETURN(mysqlnd_stream_write_w_header(conn, buffer, p - buffer - MYSQLND_HEADER_SIZE TSRMLS_CC));
+ DBG_RETURN(conn->net->m.send(conn, buffer, p - buffer - MYSQLND_HEADER_SIZE TSRMLS_CC));
}
/* }}} */
@@ -1069,7 +727,7 @@ size_t php_mysqlnd_cmd_write(void *_packet, MYSQLND *conn TSRMLS_DC)
char buffer[MYSQLND_HEADER_SIZE + 1];
int1store(buffer + MYSQLND_HEADER_SIZE, packet->command);
- written = mysqlnd_stream_write_w_header(conn, buffer, 1 TSRMLS_CC);
+ written = conn->net->m.send(conn, buffer, 1 TSRMLS_CC);
} else {
size_t tmp_len = packet->arg_len + 1 + MYSQLND_HEADER_SIZE, ret;
zend_uchar *tmp, *p;
@@ -1081,7 +739,7 @@ size_t php_mysqlnd_cmd_write(void *_packet, MYSQLND *conn TSRMLS_DC)
memcpy(p, packet->argument, packet->arg_len);
- ret = mysqlnd_stream_write_w_header(conn, (char *)tmp, tmp_len - MYSQLND_HEADER_SIZE TSRMLS_CC);
+ ret = conn->net->m.send(conn, (char *)tmp, tmp_len - MYSQLND_HEADER_SIZE TSRMLS_CC);
if (tmp != net->cmd_buffer.buffer) {
MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CMD_BUFFER_TOO_SMALL);
mnd_efree(tmp);
@@ -1404,6 +1062,7 @@ void php_mysqlnd_rset_field_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
/* }}} */
+/* {{{ php_mysqlnd_read_row_ex */
static enum_func_status
php_mysqlnd_read_row_ex(MYSQLND *conn, MYSQLND_MEMORY_POOL * result_set_memory_pool,
MYSQLND_MEMORY_POOL_CHUNK **buffer,
@@ -1459,7 +1118,7 @@ php_mysqlnd_read_row_ex(MYSQLND *conn, MYSQLND_MEMORY_POOL * result_set_memory_p
p = (*buffer)->ptr + (*data_size - header.size);
}
- if ((ret = mysqlnd_read_body(conn, &header, p TSRMLS_CC))) {
+ if ((ret = conn->net->m.receive(conn, p, header.size TSRMLS_CC))) {
DBG_ERR("Empty row packet body");
php_error(E_WARNING, "Empty row packet body");
break;
@@ -1476,6 +1135,7 @@ php_mysqlnd_read_row_ex(MYSQLND *conn, MYSQLND_MEMORY_POOL * result_set_memory_p
*data_size -= prealloc_more_bytes;
DBG_RETURN(ret);
}
+/* }}} */
/* {{{ php_mysqlnd_rowp_read_binary_protocol */
@@ -1915,7 +1575,6 @@ void php_mysqlnd_rowp_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
/* }}} */
-
/* {{{ php_mysqlnd_stats_read */
static enum_func_status
php_mysqlnd_stats_read(void *_packet, MYSQLND *conn TSRMLS_DC)