diff options
Diffstat (limited to 'ext/mysqlnd/mysqlnd_wireprotocol.c')
-rw-r--r-- | ext/mysqlnd/mysqlnd_wireprotocol.c | 194 |
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 - */ |