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.c194
1 files changed, 152 insertions, 42 deletions
diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c
index 6459fe4964..b9a91c900e 100644
--- a/ext/mysqlnd/mysqlnd_wireprotocol.c
+++ b/ext/mysqlnd/mysqlnd_wireprotocol.c
@@ -2,7 +2,7 @@
+----------------------------------------------------------------------+
| PHP Version 7 |
+----------------------------------------------------------------------+
- | Copyright (c) 2006-2018 The PHP Group |
+ | Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
@@ -287,7 +287,8 @@ mysqlnd_read_packet_header_and_body(MYSQLND_PACKET_HEADER * packet_header,
MYSQLND_STATS * stats,
MYSQLND_ERROR_INFO * error_info,
MYSQLND_CONNECTION_STATE * connection_state,
- zend_uchar * buf, size_t buf_size, const char * const packet_type_as_text,
+ zend_uchar * const buf, const size_t buf_size,
+ const char * const packet_type_as_text,
enum mysqlnd_packet_type packet_type)
{
DBG_ENTER("mysqlnd_read_packet_header_and_body");
@@ -295,7 +296,6 @@ mysqlnd_read_packet_header_and_body(MYSQLND_PACKET_HEADER * packet_header,
if (FAIL == mysqlnd_read_header(pfc, vio, packet_header, stats, error_info)) {
SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
- php_error_docref(NULL, E_WARNING, "%s", mysqlnd_server_gone);
DBG_ERR_FMT("Can't read %s's header", packet_type_as_text);
DBG_RETURN(FAIL);
}
@@ -307,7 +307,6 @@ mysqlnd_read_packet_header_and_body(MYSQLND_PACKET_HEADER * packet_header,
if (FAIL == pfc->data->m.receive(pfc, vio, buf, packet_header->size, stats, error_info)) {
SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
- php_error_docref(NULL, E_WARNING, "%s", mysqlnd_server_gone);
DBG_ERR_FMT("Empty '%s' packet body", packet_type_as_text);
DBG_RETURN(FAIL);
}
@@ -620,10 +619,14 @@ size_t php_mysqlnd_auth_write(MYSQLND_CONN_DATA * conn, void * _packet)
const MYSQLND_CSTRING payload = {(char*) buffer + MYSQLND_HEADER_SIZE, p - (buffer + MYSQLND_HEADER_SIZE)};
const unsigned int silent = packet->silent;
- ret = conn->run_command(COM_CHANGE_USER, conn, payload, silent);
+ ret = conn->command->change_user(conn, payload, silent);
DBG_RETURN(ret == PASS? (p - buffer - MYSQLND_HEADER_SIZE) : 0);
} else {
- size_t sent = pfc->data->m.send(pfc, vio, buffer, p - buffer - MYSQLND_HEADER_SIZE, stats, error_info);
+ /*
+ The auth handshake packet has no command in it. Thus we can't go over conn->command directly.
+ Well, we can have a command->no_command(conn, payload)
+ */
+ const size_t sent = pfc->data->m.send(pfc, vio, buffer, p - buffer - MYSQLND_HEADER_SIZE, stats, error_info);
if (!sent) {
SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
}
@@ -768,7 +771,7 @@ php_mysqlnd_change_auth_response_write(MYSQLND_CONN_DATA * conn, void * _packet)
MYSQLND_VIO * vio = conn->vio;
MYSQLND_STATS * stats = conn->stats;
MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
- zend_uchar * buffer = pfc->cmd_buffer.length >= packet->auth_data_len? pfc->cmd_buffer.buffer : mnd_emalloc(packet->auth_data_len);
+ zend_uchar * const buffer = pfc->cmd_buffer.length >= packet->auth_data_len? pfc->cmd_buffer.buffer : mnd_emalloc(packet->auth_data_len);
zend_uchar * p = buffer + MYSQLND_HEADER_SIZE; /* start after the header */
DBG_ENTER("php_mysqlnd_change_auth_response_write");
@@ -779,7 +782,11 @@ php_mysqlnd_change_auth_response_write(MYSQLND_CONN_DATA * conn, void * _packet)
}
{
- size_t sent = pfc->data->m.send(pfc, vio, buffer, p - buffer - MYSQLND_HEADER_SIZE, stats, error_info);
+ /*
+ The auth handshake packet has no command in it. Thus we can't go over conn->command directly.
+ Well, we can have a command->no_command(conn, payload)
+ */
+ const size_t sent = pfc->data->m.send(pfc, vio, buffer, p - buffer - MYSQLND_HEADER_SIZE, stats, error_info);
if (buffer != pfc->cmd_buffer.buffer) {
mnd_efree(buffer);
}
@@ -805,8 +812,8 @@ php_mysqlnd_ok_read(MYSQLND_CONN_DATA * conn, void * _packet)
MYSQLND_STATS * stats = conn->stats;
MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
zend_uchar local_buf[OK_BUFFER_SIZE];
- size_t buf_len = pfc->cmd_buffer.buffer? pfc->cmd_buffer.length : OK_BUFFER_SIZE;
- zend_uchar * buf = pfc->cmd_buffer.buffer? (zend_uchar *) pfc->cmd_buffer.buffer : local_buf;
+ const size_t buf_len = pfc->cmd_buffer.buffer? pfc->cmd_buffer.length : OK_BUFFER_SIZE;
+ zend_uchar * const buf = pfc->cmd_buffer.buffer? (zend_uchar *) pfc->cmd_buffer.buffer : local_buf;
const zend_uchar * p = buf;
const zend_uchar * const begin = buf;
zend_ulong net_len;
@@ -899,8 +906,8 @@ php_mysqlnd_eof_read(MYSQLND_CONN_DATA * conn, void * _packet)
MYSQLND_VIO * vio = conn->vio;
MYSQLND_STATS * stats = conn->stats;
MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
- size_t buf_len = pfc->cmd_buffer.length;
- zend_uchar * buf = (zend_uchar *) pfc->cmd_buffer.buffer;
+ const size_t buf_len = pfc->cmd_buffer.length;
+ zend_uchar * const buf = (zend_uchar *) pfc->cmd_buffer.buffer;
const zend_uchar * p = buf;
const zend_uchar * const begin = buf;
@@ -967,7 +974,7 @@ size_t php_mysqlnd_cmd_write(MYSQLND_CONN_DATA * conn, void * _packet)
MYSQLND_VIO * vio = conn->vio;
MYSQLND_STATS * stats = conn->stats;
MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
- unsigned int error_reporting = EG(error_reporting);
+ const unsigned int error_reporting = EG(error_reporting);
size_t sent = 0;
DBG_ENTER("php_mysqlnd_cmd_write");
@@ -1036,8 +1043,8 @@ php_mysqlnd_rset_header_read(MYSQLND_CONN_DATA * conn, void * _packet)
MYSQLND_STATS * stats = conn->stats;
MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
enum_func_status ret = PASS;
- size_t buf_len = pfc->cmd_buffer.length;
- zend_uchar * buf = (zend_uchar *) pfc->cmd_buffer.buffer;
+ const size_t buf_len = pfc->cmd_buffer.length;
+ zend_uchar * const buf = (zend_uchar *) pfc->cmd_buffer.buffer;
const zend_uchar * p = buf;
const zend_uchar * const begin = buf;
size_t len;
@@ -1177,8 +1184,9 @@ php_mysqlnd_rset_field_read(MYSQLND_CONN_DATA * conn, void * _packet)
MYSQLND_VIO * vio = conn->vio;
MYSQLND_STATS * stats = conn->stats;
MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
- size_t buf_len = pfc->cmd_buffer.length, total_len = 0;
- zend_uchar * buf = (zend_uchar *) pfc->cmd_buffer.buffer;
+ const size_t buf_len = pfc->cmd_buffer.length;
+ size_t total_len = 0;
+ zend_uchar * const buf = (zend_uchar *) pfc->cmd_buffer.buffer;
const zend_uchar * p = buf;
const zend_uchar * const begin = buf;
char *root_ptr;
@@ -1355,7 +1363,7 @@ php_mysqlnd_read_row_ex(MYSQLND_PFC * pfc,
MYSQLND_ERROR_INFO * error_info,
MYSQLND_MEMORY_POOL * pool,
MYSQLND_ROW_BUFFER * buffer,
- size_t * data_size)
+ size_t * const data_size)
{
enum_func_status ret = PASS;
MYSQLND_PACKET_HEADER header;
@@ -1435,8 +1443,8 @@ php_mysqlnd_read_row_ex(MYSQLND_PFC * pfc,
/* {{{ php_mysqlnd_rowp_read_binary_protocol */
enum_func_status
php_mysqlnd_rowp_read_binary_protocol(MYSQLND_ROW_BUFFER * row_buffer, zval * fields,
- unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
- zend_bool as_int_or_float, MYSQLND_STATS * stats)
+ const unsigned int field_count, const MYSQLND_FIELD * const fields_metadata,
+ const zend_bool as_int_or_float, MYSQLND_STATS * const stats)
{
unsigned int i;
const zend_uchar * p = row_buffer->ptr;
@@ -1532,7 +1540,7 @@ php_mysqlnd_rowp_read_text_protocol_aux(MYSQLND_ROW_BUFFER * row_buffer, zval *
unsigned int i;
zval *current_field, *end_field, *start_field;
zend_uchar * p = row_buffer->ptr;
- size_t data_size = row_buffer->size;
+ const size_t data_size = row_buffer->size;
const zend_uchar * const packet_end = (zend_uchar*) p + data_size;
DBG_ENTER("php_mysqlnd_rowp_read_text_protocol_aux");
@@ -1655,7 +1663,7 @@ php_mysqlnd_rowp_read_text_protocol_aux(MYSQLND_ROW_BUFFER * row_buffer, zval *
if (Z_TYPE_P(current_field) == IS_LONG && !as_int_or_float) {
/* we are using the text protocol, so convert to string */
char tmp[22];
- const size_t tmp_len = sprintf((char *)&tmp, MYSQLND_LLU_SPEC, (uint64_t) Z_LVAL_P(current_field));
+ const size_t tmp_len = sprintf((char *)&tmp, ZEND_ULONG_FMT, Z_LVAL_P(current_field));
ZVAL_STRINGL(current_field, tmp, tmp_len);
} else if (Z_TYPE_P(current_field) == IS_STRING) {
/* nothing to do here, as we want a string and ps_fetch_from_1_to_8_bytes() has given us one */
@@ -1679,8 +1687,8 @@ php_mysqlnd_rowp_read_text_protocol_aux(MYSQLND_ROW_BUFFER * row_buffer, zval *
/* {{{ php_mysqlnd_rowp_read_text_protocol_zval */
enum_func_status
php_mysqlnd_rowp_read_text_protocol_zval(MYSQLND_ROW_BUFFER * row_buffer, zval * fields,
- unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
- zend_bool as_int_or_float, MYSQLND_STATS * stats)
+ const unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
+ const zend_bool as_int_or_float, MYSQLND_STATS * stats)
{
enum_func_status ret;
DBG_ENTER("php_mysqlnd_rowp_read_text_protocol_zval");
@@ -1693,8 +1701,8 @@ php_mysqlnd_rowp_read_text_protocol_zval(MYSQLND_ROW_BUFFER * row_buffer, zval *
/* {{{ php_mysqlnd_rowp_read_text_protocol_c */
enum_func_status
php_mysqlnd_rowp_read_text_protocol_c(MYSQLND_ROW_BUFFER * row_buffer, zval * fields,
- unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
- zend_bool as_int_or_float, MYSQLND_STATS * stats)
+ const unsigned int field_count, const MYSQLND_FIELD * const fields_metadata,
+ const zend_bool as_int_or_float, MYSQLND_STATS * const stats)
{
enum_func_status ret;
DBG_ENTER("php_mysqlnd_rowp_read_text_protocol_c");
@@ -1838,7 +1846,7 @@ php_mysqlnd_stats_read(MYSQLND_CONN_DATA * conn, void * _packet)
MYSQLND_VIO * vio = conn->vio;
MYSQLND_STATS * stats = conn->stats;
MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
- size_t buf_len = pfc->cmd_buffer.length;
+ const size_t buf_len = pfc->cmd_buffer.length;
zend_uchar *buf = (zend_uchar *) pfc->cmd_buffer.buffer;
DBG_ENTER("php_mysqlnd_stats_read");
@@ -1885,7 +1893,7 @@ php_mysqlnd_prepare_read(MYSQLND_CONN_DATA * conn, void * _packet)
MYSQLND_STATS * stats = conn->stats;
MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
/* In case of an error, we should have place to put it */
- size_t buf_len = pfc->cmd_buffer.length;
+ const size_t buf_len = pfc->cmd_buffer.length;
zend_uchar *buf = (zend_uchar *) pfc->cmd_buffer.buffer;
zend_uchar *p = buf;
const zend_uchar * const begin = buf;
@@ -1968,7 +1976,7 @@ php_mysqlnd_chg_user_read(MYSQLND_CONN_DATA * conn, void * _packet)
MYSQLND_STATS * stats = conn->stats;
MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
/* There could be an error message */
- size_t buf_len = pfc->cmd_buffer.length;
+ const size_t buf_len = pfc->cmd_buffer.length;
zend_uchar *buf = (zend_uchar *) pfc->cmd_buffer.buffer;
zend_uchar *p = buf;
const zend_uchar * const begin = buf;
@@ -2125,6 +2133,101 @@ php_mysqlnd_sha256_pk_request_response_free_mem(void * _packet)
}
/* }}} */
+static
+size_t php_mysqlnd_cached_sha2_result_write(MYSQLND_CONN_DATA * conn, void * _packet)
+{
+ MYSQLND_PACKET_CACHED_SHA2_RESULT * packet= (MYSQLND_PACKET_CACHED_SHA2_RESULT *) _packet;
+ MYSQLND_ERROR_INFO * error_info = conn->error_info;
+ MYSQLND_PFC * pfc = conn->protocol_frame_codec;
+ MYSQLND_VIO * vio = conn->vio;
+ MYSQLND_STATS * stats = conn->stats;
+ ALLOCA_FLAG(use_heap)
+ zend_uchar *buffer = do_alloca(MYSQLND_HEADER_SIZE + packet->password_len + 1, use_heap);
+ size_t sent;
+
+ DBG_ENTER("php_mysqlnd_cached_sha2_result_write");
+
+ if (packet->request == 1) {
+ int1store(buffer + MYSQLND_HEADER_SIZE, '\2');
+ sent = pfc->data->m.send(pfc, vio, buffer, 1, stats, error_info);
+ } else {
+ memcpy(buffer + MYSQLND_HEADER_SIZE, packet->password, packet->password_len);
+ sent = pfc->data->m.send(pfc, vio, buffer, packet->password_len, stats, error_info);
+ }
+
+ free_alloca(buffer, use_heap);
+ DBG_RETURN(sent);
+}
+
+static enum_func_status
+php_mysqlnd_cached_sha2_result_read(MYSQLND_CONN_DATA * conn, void * _packet)
+{
+ MYSQLND_PACKET_CACHED_SHA2_RESULT * packet= (MYSQLND_PACKET_CACHED_SHA2_RESULT *) _packet;
+ MYSQLND_ERROR_INFO * error_info = conn->error_info;
+ MYSQLND_PFC * pfc = conn->protocol_frame_codec;
+ MYSQLND_VIO * vio = conn->vio;
+ MYSQLND_STATS * stats = conn->stats;
+ MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
+ zend_uchar buf[SHA256_PK_REQUEST_RESP_BUFFER_SIZE];
+ zend_uchar *p = buf;
+ const zend_uchar * const begin = buf;
+
+ DBG_ENTER("php_mysqlnd_cached_sha2_result_read");
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, sizeof(buf), "PROT_CACHED_SHA2_RESULT_PACKET", PROT_CACHED_SHA2_RESULT_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
+ BAIL_IF_NO_MORE_DATA;
+
+ packet->response_code = uint1korr(p);
+ p++;
+ BAIL_IF_NO_MORE_DATA;
+
+ if (ERROR_MARKER == packet->response_code) {
+ php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
+ packet->error, sizeof(packet->error),
+ &packet->error_no, packet->sqlstate
+ );
+ DBG_RETURN(PASS);
+ }
+ if (0xFE == packet->response_code) {
+ /* Authentication Switch Response */
+ if (packet->header.size > (size_t) (p - buf)) {
+ packet->new_auth_protocol = mnd_pestrdup((char *)p, FALSE);
+ packet->new_auth_protocol_len = strlen(packet->new_auth_protocol);
+ p+= packet->new_auth_protocol_len + 1; /* +1 for the \0 */
+
+ packet->new_auth_protocol_data_len = packet->header.size - (size_t) (p - buf);
+ if (packet->new_auth_protocol_data_len) {
+ packet->new_auth_protocol_data = mnd_emalloc(packet->new_auth_protocol_data_len);
+ memcpy(packet->new_auth_protocol_data, p, packet->new_auth_protocol_data_len);
+ }
+ DBG_INF_FMT("The server requested switching auth plugin to : %s", packet->new_auth_protocol);
+ DBG_INF_FMT("Server salt : [%d][%.*s]", packet->new_auth_protocol_data_len, packet->new_auth_protocol_data_len, packet->new_auth_protocol_data);
+ }
+ DBG_RETURN(PASS);
+ }
+
+ if (0x1 != packet->response_code) {
+ DBG_ERR_FMT("Unexpected response code %d", packet->response_code);
+ }
+
+ /* This is not really the response code, but we reuse the field. */
+ packet->response_code = uint1korr(p);
+ p++;
+ BAIL_IF_NO_MORE_DATA;
+
+ packet->result = uint1korr(p);
+ BAIL_IF_NO_MORE_DATA;
+
+ DBG_RETURN(PASS);
+
+premature_end:
+ DBG_ERR_FMT("OK packet %d bytes shorter than expected", p - begin - packet->header.size);
+ php_error_docref(NULL, E_WARNING, "SHA256_PK_REQUEST_RESPONSE packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
+ p - begin - packet->header.size);
+ DBG_RETURN(FAIL);
+}
+
/* {{{ packet_methods */
static
mysqlnd_packet_methods packet_methods[PROT_LAST] =
@@ -2203,7 +2306,12 @@ mysqlnd_packet_methods packet_methods[PROT_LAST] =
php_mysqlnd_sha256_pk_request_response_read,
NULL, /* write */
php_mysqlnd_sha256_pk_request_response_free_mem,
- } /* PROT_SHA256_PK_REQUEST_RESPONSE_PACKET */
+ }, /* PROT_SHA256_PK_REQUEST_RESPONSE_PACKET */
+ {
+ php_mysqlnd_cached_sha2_result_read,
+ php_mysqlnd_cached_sha2_result_write,
+ NULL
+ } /* PROT_CACHED_SHA2_RESULT_PACKET */
};
/* }}} */
@@ -2387,6 +2495,17 @@ MYSQLND_METHOD(mysqlnd_protocol, init_sha256_pk_request_response_packet)(struct
}
/* }}} */
+/* {{{ mysqlnd_protocol::init_cached_sha2_result_packet */
+static void
+MYSQLND_METHOD(mysqlnd_protocol, init_cached_sha2_result_packet)(struct st_mysqlnd_packet_cached_sha2_result *packet)
+{
+ DBG_ENTER("mysqlnd_protocol::init_cached_sha2_result_packet");
+ memset(packet, 0, sizeof(*packet));
+ packet->header.m = &packet_methods[PROT_CACHED_SHA2_RESULT_PACKET];
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
/* {{{ mysqlnd_protocol::send_command */
static enum_func_status
@@ -2397,7 +2516,7 @@ MYSQLND_METHOD(mysqlnd_protocol, send_command)(
const zend_bool silent,
struct st_mysqlnd_connection_state * connection_state,
- MYSQLND_ERROR_INFO * error_info,
+ MYSQLND_ERROR_INFO * error_info,
MYSQLND_UPSERT_STATUS * upsert_status,
MYSQLND_STATS * stats,
func_mysqlnd_conn_data__send_close send_close,
@@ -2439,7 +2558,7 @@ MYSQLND_METHOD(mysqlnd_protocol, send_command)(
MYSQLND_INC_CONN_STATISTIC(stats, STAT_COM_QUIT + command - 1 /* because of COM_SLEEP */ );
if (! PACKET_WRITE(payload_decoder_factory->conn, &cmd_packet)) {
- if (!silent) {
+ if (!silent && error_info->error_no != CR_SERVER_GONE_ERROR) {
DBG_ERR_FMT("Error while sending %s packet", mysqlnd_command_to_text[command]);
php_error(E_WARNING, "Error while sending %s packet. PID=%d", mysqlnd_command_to_text[command], getpid());
}
@@ -2605,6 +2724,7 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_protocol_payload_decoder_factory)
MYSQLND_METHOD(mysqlnd_protocol, init_change_user_response_packet),
MYSQLND_METHOD(mysqlnd_protocol, init_sha256_pk_request_packet),
MYSQLND_METHOD(mysqlnd_protocol, init_sha256_pk_request_response_packet),
+ MYSQLND_METHOD(mysqlnd_protocol, init_cached_sha2_result_packet),
MYSQLND_METHOD(mysqlnd_protocol, send_command),
MYSQLND_METHOD(mysqlnd_protocol, send_command_handle_response),
@@ -2638,13 +2758,3 @@ mysqlnd_protocol_payload_decoder_factory_free(MYSQLND_PROTOCOL_PAYLOAD_DECODER_F
DBG_VOID_RETURN;
}
/* }}} */
-
-
-/*
- * Local variables:
- * tab-width: 4
- * c-basic-offset: 4
- * End:
- * vim600: noet sw=4 ts=4 fdm=marker
- * vim<600: noet sw=4 ts=4
- */