Пример #1
0
/* {{{ mysqlnd_com_statistics_run */
static enum_func_status
mysqlnd_com_statistics_run(void *cmd)
{
	struct st_mysqlnd_protocol_com_statistics_command * command = (struct st_mysqlnd_protocol_com_statistics_command *) cmd;
	enum_func_status ret = FAIL;
	MYSQLND_CONN_DATA * conn = command->context.conn;
	zend_string **message = command->context.message;
	func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;

	DBG_ENTER("mysqlnd_com_statistics_run");

	ret = send_command(conn->payload_decoder_factory, COM_STATISTICS, NULL, 0, FALSE,
					   &conn->state,
					   conn->error_info,
					   conn->upsert_status,
					   conn->stats,
					   conn->m->send_close,
					   conn);

	if (PASS == ret) {
		MYSQLND_PACKET_STATS * stats_header = conn->payload_decoder_factory->m.get_stats_packet(conn->payload_decoder_factory, FALSE);
		if (!stats_header) {
			SET_OOM_ERROR(conn->error_info);
		} else {
			if (PASS == (ret = PACKET_READ(stats_header))) {
				/* will be freed by Zend, thus don't use the mnd_ allocator */
				*message = zend_string_init(stats_header->message.s, stats_header->message.l, 0);
				DBG_INF(ZSTR_VAL(*message));
			}
			PACKET_FREE(stats_header);
		}
	}

	DBG_RETURN(ret);
}
Пример #2
0
/* {{{ mysqlnd_object_factory::get_prepared_statement */
static MYSQLND_STMT *
MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement)(MYSQLND_CONN_DATA * const conn)
{
	size_t alloc_size = sizeof(MYSQLND_STMT) + mysqlnd_plugin_count() * sizeof(void *);
	MYSQLND_STMT * ret = mnd_pecalloc(1, alloc_size, conn->persistent);
	MYSQLND_STMT_DATA * stmt = NULL;

	DBG_ENTER("mysqlnd_object_factory::get_prepared_statement");
	do {
		if (!ret) {
			break;
		}
		ret->m = mysqlnd_stmt_get_methods();
		ret->persistent = conn->persistent;

		stmt = ret->data = mnd_pecalloc(1, sizeof(MYSQLND_STMT_DATA), conn->persistent);
		DBG_INF_FMT("stmt=%p", stmt);
		if (!stmt) {
			break;
		}
		stmt->persistent = conn->persistent;
		stmt->error_info = &(stmt->error_info_impl);
		stmt->upsert_status = &(stmt->upsert_status_impl);
		stmt->state = MYSQLND_STMT_INITTED;
		stmt->execute_cmd_buffer.length = 4096;
		stmt->execute_cmd_buffer.buffer = mnd_pemalloc(stmt->execute_cmd_buffer.length, stmt->persistent);
		if (!stmt->execute_cmd_buffer.buffer) {
			break;
		}

		stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS;
		/*
		  Mark that we reference the connection, thus it won't be
		  be destructed till there is open statements. The last statement
		  or normal query result will close it then.
		*/
		stmt->conn = conn->m->get_reference(conn);
		stmt->error_info->error_list = mnd_pecalloc(1, sizeof(zend_llist), ret->persistent);
		if (!stmt->error_info->error_list) {
			break;
		}

		zend_llist_init(stmt->error_info->error_list, sizeof(MYSQLND_ERROR_LIST_ELEMENT), (llist_dtor_func_t) mysqlnd_error_list_pdtor, conn->persistent);

		DBG_RETURN(ret);
	} while (0);

	SET_OOM_ERROR(*conn->error_info);
	if (ret) {
		ret->m->dtor(ret, TRUE);
		ret = NULL;
	}
	DBG_RETURN(NULL);
}
Пример #3
0
/* {{{ mysqlnd_stmt_execute_prepare_param_types */
static enum_func_status
mysqlnd_stmt_execute_prepare_param_types(MYSQLND_STMT_DATA * stmt, zval ** copies_param, int * resend_types_next_time)
{
	unsigned int i;
	DBG_ENTER("mysqlnd_stmt_execute_prepare_param_types");
	for (i = 0; i < stmt->param_count; i++) {
		short current_type = stmt->param_bind[i].type;
		zval *parameter = &stmt->param_bind[i].zv;

		ZVAL_DEREF(parameter);
		if (!Z_ISNULL_P(parameter) && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) {
			/* always copy the var, because we do many conversions */
			if (Z_TYPE_P(parameter) != IS_LONG &&
				PASS != mysqlnd_stmt_copy_it(copies_param, parameter, stmt->param_count, i))
			{
				SET_OOM_ERROR(*stmt->error_info);
				goto end;
			}
			/*
			  if it doesn't fit in a long send it as a string.
			  Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
			*/
			if (Z_TYPE_P(parameter) != IS_LONG) {
				zval *tmp_data = (*copies_param && !Z_ISUNDEF((*copies_param)[i]))? &(*copies_param)[i]: parameter;
				/*
				  Because converting to double and back to long can lead
				  to losing precision we need second variable. Conversion to double is to see if
				  value is too big for a long. As said, precision could be lost.
				*/
				zval tmp_data_copy;
				ZVAL_COPY(&tmp_data_copy, tmp_data);
				convert_to_double_ex(&tmp_data_copy);

				/*
				  if it doesn't fit in a long send it as a string.
				  Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
				  We do transformation here, which will be used later when sending types. The code later relies on this.
				*/
				if (Z_DVAL(tmp_data_copy) > ZEND_LONG_MAX || Z_DVAL(tmp_data_copy) < ZEND_LONG_MIN) {
					stmt->send_types_to_server = *resend_types_next_time = 1;
					convert_to_string_ex(tmp_data);
				} else {
					convert_to_long_ex(tmp_data);
				}

				zval_ptr_dtor(&tmp_data_copy);
			}
		}
	}
	DBG_RETURN(PASS);
end:
	DBG_RETURN(FAIL);
}
Пример #4
0
/* {{{ mysqlnd_res_meta::read_metadata */
static enum_func_status
MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const meta, MYSQLND_CONN_DATA * conn)
{
	unsigned int i = 0;
	MYSQLND_PACKET_RES_FIELD * field_packet;

	DBG_ENTER("mysqlnd_res_meta::read_metadata");

	field_packet = conn->payload_decoder_factory->m.get_result_field_packet(conn->payload_decoder_factory, FALSE);
	if (!field_packet) {
		SET_OOM_ERROR(conn->error_info);
		DBG_RETURN(FAIL);
	}
	field_packet->persistent_alloc = meta->persistent;
	for (;i < meta->field_count; i++) {
		zend_ulong idx;

		if (meta->fields[i].root) {
			/* We re-read metadata for PS */
			mnd_pefree(meta->fields[i].root, meta->persistent);
			meta->fields[i].root = NULL;
		}

		field_packet->metadata = &(meta->fields[i]);
		if (FAIL == PACKET_READ(field_packet)) {
			PACKET_FREE(field_packet);
			DBG_RETURN(FAIL);
		}
		if (field_packet->error_info.error_no) {
			COPY_CLIENT_ERROR(conn->error_info, field_packet->error_info);
			/* Return back from CONN_QUERY_SENT */
			PACKET_FREE(field_packet);
			DBG_RETURN(FAIL);
		}

		if (mysqlnd_ps_fetch_functions[meta->fields[i].type].func == NULL) {
			DBG_ERR_FMT("Unknown type %u sent by the server.  Please send a report to the developers", meta->fields[i].type);
			php_error_docref(NULL, E_WARNING, "Unknown type %u sent by the server. Please send a report to the developers", meta->fields[i].type);
			PACKET_FREE(field_packet);
			DBG_RETURN(FAIL);
		}

		/* For BC we have to check whether the key is numeric and use it like this */
		if ((meta->zend_hash_keys[i].is_numeric = ZEND_HANDLE_NUMERIC(field_packet->metadata->sname, idx))) {
			meta->zend_hash_keys[i].key = idx;
		}
	}
	PACKET_FREE(field_packet);

	DBG_RETURN(PASS);
}
Пример #5
0
/* {{{ mysqlnd_com_init_db_run */
static enum_func_status
mysqlnd_com_init_db_run(void *cmd)
{
	struct st_mysqlnd_protocol_com_init_db_command * command = (struct st_mysqlnd_protocol_com_init_db_command *) cmd;
	enum_func_status ret = FAIL;
	MYSQLND_CONN_DATA * conn = command->context.conn;
	const MYSQLND_CSTRING db = command->context.db;
	func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
	func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response send_command_handle_response = conn->payload_decoder_factory->m.send_command_handle_response;

	DBG_ENTER("mysqlnd_com_init_db_run");

	ret = send_command(conn->payload_decoder_factory, COM_INIT_DB, (zend_uchar*) command->context.db.s, command->context.db.l, FALSE,
					   &conn->state,
					   conn->error_info,
					   conn->upsert_status,
					   conn->stats,
					   conn->m->send_close,
					   conn);
	if (PASS == ret) {
		ret = send_command_handle_response(conn->payload_decoder_factory, PROT_OK_PACKET, FALSE, COM_INIT_DB, TRUE,
										   conn->error_info, conn->upsert_status, &conn->last_message, conn->persistent);
	}

	/*
	  The server sends 0 but libmysql doesn't read it and has established
	  a protocol of giving back -1. Thus we have to follow it :(
	*/
	UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
	if (ret == PASS) {
		if (conn->connect_or_select_db.s) {
			mnd_pefree(conn->connect_or_select_db.s, conn->persistent);
		}
		conn->connect_or_select_db.s = mnd_pestrndup(db.s, db.l, conn->persistent);
		conn->connect_or_select_db.l = db.l;
		if (!conn->connect_or_select_db.s) {
			/* OOM */
			SET_OOM_ERROR(conn->error_info);
			ret = FAIL;
		}
	}

	DBG_RETURN(ret);
}
Пример #6
0
/* {{{ mysqlnd_sha256_get_rsa_key */
static RSA *
mysqlnd_sha256_get_rsa_key(MYSQLND_CONN_DATA * conn,
						   const MYSQLND_OPTIONS * const options,
						   const MYSQLND_NET_OPTIONS * const net_options
						  )
{
	RSA * ret = NULL;
	const char * fname = (net_options->sha256_server_public_key && net_options->sha256_server_public_key[0] != '\0')?
								net_options->sha256_server_public_key:
								MYSQLND_G(sha256_server_public_key);
	php_stream * stream;
	DBG_ENTER("mysqlnd_sha256_get_rsa_key");
	DBG_INF_FMT("options_s256_pk=[%s] MYSQLND_G(sha256_server_public_key)=[%s]",
				 net_options->sha256_server_public_key? net_options->sha256_server_public_key:"n/a",
				 MYSQLND_G(sha256_server_public_key)? MYSQLND_G(sha256_server_public_key):"n/a");
	if (!fname || fname[0] == '\0') {
		MYSQLND_PACKET_SHA256_PK_REQUEST * pk_req_packet = NULL;
		MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE * pk_resp_packet = NULL;

		do {
			DBG_INF("requesting the public key from the server");
			pk_req_packet = conn->protocol->m.get_sha256_pk_request_packet(conn->protocol, FALSE);
			if (!pk_req_packet) {
				SET_OOM_ERROR(*conn->error_info);
				break;
			}
			pk_resp_packet = conn->protocol->m.get_sha256_pk_request_response_packet(conn->protocol, FALSE);
			if (!pk_resp_packet) {
				SET_OOM_ERROR(*conn->error_info);
				PACKET_FREE(pk_req_packet);
				break;
			}

			if (! PACKET_WRITE(pk_req_packet, conn)) {
				DBG_ERR_FMT("Error while sending public key request packet");
				php_error(E_WARNING, "Error while sending public key request packet. PID=%d", getpid());
				CONN_SET_STATE(conn, CONN_QUIT_SENT);
				break;
			}
			if (FAIL == PACKET_READ(pk_resp_packet, conn) || NULL == pk_resp_packet->public_key) {
				DBG_ERR_FMT("Error while receiving public key");
				php_error(E_WARNING, "Error while receiving public key. PID=%d", getpid());
				CONN_SET_STATE(conn, CONN_QUIT_SENT);
				break;
			}
			DBG_INF_FMT("Public key(%d):\n%s", pk_resp_packet->public_key_len, pk_resp_packet->public_key);
			/* now extract the public key */
			{
				BIO * bio = BIO_new_mem_buf(pk_resp_packet->public_key, pk_resp_packet->public_key_len);
				ret = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL);
				BIO_free(bio);
			}
		} while (0);
		PACKET_FREE(pk_req_packet);
		PACKET_FREE(pk_resp_packet);

		DBG_INF_FMT("ret=%p", ret);
		DBG_RETURN(ret);

		SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE,
			"sha256_server_public_key is not set for the connection or as mysqlnd.sha256_server_public_key");
		DBG_ERR("server_public_key is not set");
		DBG_RETURN(NULL);
	} else {
		zend_string * key_str;
		DBG_INF_FMT("Key in a file. [%s]", fname);
		stream = php_stream_open_wrapper((char *) fname, "rb", REPORT_ERRORS, NULL);

		if (stream) {
			if ((key_str = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0)) != NULL) {
				BIO * bio = BIO_new_mem_buf(key_str->val, key_str->len);
				ret = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL);
				BIO_free(bio);
				DBG_INF("Successfully loaded");
				DBG_INF_FMT("Public key:%*.s", key_str->len, key_str->val);
				zend_string_release(key_str);
			}
			php_stream_free(stream, PHP_STREAM_FREE_CLOSE);
		}
	}
	DBG_RETURN(ret);
}
Пример #7
0
/* {{{ mysqlnd_auth_handshake */
enum_func_status
mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
							  const char * const user,
							  const char * const passwd,
							  const size_t passwd_len,
							  const char * const db,
							  const size_t db_len,
							  const MYSQLND_OPTIONS * const options,
							  zend_ulong mysql_flags,
							  unsigned int server_charset_no,
							  zend_bool use_full_blown_auth_packet,
							  const char * const auth_protocol,
							  const zend_uchar * const auth_plugin_data,
							  const size_t auth_plugin_data_len,
							  char ** switch_to_auth_protocol,
							  size_t * switch_to_auth_protocol_len,
							  zend_uchar ** switch_to_auth_protocol_data,
							  size_t * switch_to_auth_protocol_data_len
							 )
{
	enum_func_status ret = FAIL;
	const MYSQLND_CHARSET * charset = NULL;
	MYSQLND_PACKET_CHANGE_AUTH_RESPONSE * change_auth_resp_packet = NULL;
	MYSQLND_PACKET_AUTH_RESPONSE * auth_resp_packet = NULL;
	MYSQLND_PACKET_AUTH * auth_packet = NULL;

	DBG_ENTER("mysqlnd_auth_handshake");

	auth_resp_packet = conn->protocol->m.get_auth_response_packet(conn->protocol, FALSE);

	if (!auth_resp_packet) {
		SET_OOM_ERROR(*conn->error_info);
		goto end;
	}

	if (use_full_blown_auth_packet != TRUE) {
		change_auth_resp_packet = conn->protocol->m.get_change_auth_response_packet(conn->protocol, FALSE);
		if (!change_auth_resp_packet) {
			SET_OOM_ERROR(*conn->error_info);
			goto end;
		}

		change_auth_resp_packet->auth_data = auth_plugin_data;
		change_auth_resp_packet->auth_data_len = auth_plugin_data_len;

		if (!PACKET_WRITE(change_auth_resp_packet, conn)) {
			CONN_SET_STATE(conn, CONN_QUIT_SENT);
			SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
			goto end;
		}
	} else {
		auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE);

		auth_packet->client_flags = mysql_flags;
		auth_packet->max_packet_size = options->max_allowed_packet;
		if (options->charset_name && (charset = mysqlnd_find_charset_name(options->charset_name))) {
			auth_packet->charset_no	= charset->nr;
		} else {
			auth_packet->charset_no	= server_charset_no;
		}

		auth_packet->send_auth_data = TRUE;
		auth_packet->user		= user;
		auth_packet->db			= db;
		auth_packet->db_len		= db_len;

		auth_packet->auth_data = auth_plugin_data;
		auth_packet->auth_data_len = auth_plugin_data_len;
		auth_packet->auth_plugin_name = auth_protocol;

		if (conn->server_capabilities & CLIENT_CONNECT_ATTRS) {
			auth_packet->connect_attr = conn->options->connect_attr;
		}

		if (!PACKET_WRITE(auth_packet, conn)) {
			goto end;
		}
	}
	if (use_full_blown_auth_packet == TRUE) {
		conn->charset = mysqlnd_find_charset_nr(auth_packet->charset_no);
	}

	if (FAIL == PACKET_READ(auth_resp_packet, conn) || auth_resp_packet->response_code >= 0xFE) {
		if (auth_resp_packet->response_code == 0xFE) {
			/* old authentication with new server  !*/
			if (!auth_resp_packet->new_auth_protocol) {
				DBG_ERR(mysqlnd_old_passwd);
				SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
			} else {
				*switch_to_auth_protocol = mnd_pestrndup(auth_resp_packet->new_auth_protocol, auth_resp_packet->new_auth_protocol_len, FALSE);
				*switch_to_auth_protocol_len = auth_resp_packet->new_auth_protocol_len;
				if (auth_resp_packet->new_auth_protocol_data) {
					*switch_to_auth_protocol_data_len = auth_resp_packet->new_auth_protocol_data_len;
					*switch_to_auth_protocol_data = mnd_emalloc(*switch_to_auth_protocol_data_len);
					memcpy(*switch_to_auth_protocol_data, auth_resp_packet->new_auth_protocol_data, *switch_to_auth_protocol_data_len);
				} else {
					*switch_to_auth_protocol_data = NULL;
					*switch_to_auth_protocol_data_len = 0;
				}
			}
		} else if (auth_resp_packet->response_code == 0xFF) {
			if (auth_resp_packet->sqlstate[0]) {
				strlcpy(conn->error_info->sqlstate, auth_resp_packet->sqlstate, sizeof(conn->error_info->sqlstate));
				DBG_ERR_FMT("ERROR:%u [SQLSTATE:%s] %s", auth_resp_packet->error_no, auth_resp_packet->sqlstate, auth_resp_packet->error);
			}
			SET_CLIENT_ERROR(*conn->error_info, auth_resp_packet->error_no, UNKNOWN_SQLSTATE, auth_resp_packet->error);
		}
		goto end;
	}

	SET_NEW_MESSAGE(conn->last_message, conn->last_message_len, auth_resp_packet->message, auth_resp_packet->message_len, conn->persistent);
	ret = PASS;
end:
	PACKET_FREE(change_auth_resp_packet);
	PACKET_FREE(auth_packet);
	PACKET_FREE(auth_resp_packet);
	DBG_RETURN(ret);
}
Пример #8
0
/* {{{ mysqlnd_auth_change_user */
enum_func_status
mysqlnd_auth_change_user(MYSQLND_CONN_DATA * const conn,
								const char * const user,
								const size_t user_len,
								const char * const passwd,
								const size_t passwd_len,
								const char * const db,
								const size_t db_len,
								const zend_bool silent,
								zend_bool use_full_blown_auth_packet,
								const char * const auth_protocol,
								zend_uchar * auth_plugin_data,
								size_t auth_plugin_data_len,
								char ** switch_to_auth_protocol,
								size_t * switch_to_auth_protocol_len,
								zend_uchar ** switch_to_auth_protocol_data,
								size_t * switch_to_auth_protocol_data_len
								)
{
	enum_func_status ret = FAIL;
	const MYSQLND_CHARSET * old_cs = conn->charset;
	MYSQLND_PACKET_CHANGE_AUTH_RESPONSE * change_auth_resp_packet = NULL;
	MYSQLND_PACKET_CHG_USER_RESPONSE * chg_user_resp = NULL;
	MYSQLND_PACKET_AUTH * auth_packet = NULL;

	DBG_ENTER("mysqlnd_auth_change_user");

	chg_user_resp = conn->protocol->m.get_change_user_response_packet(conn->protocol, FALSE);

	if (!chg_user_resp) {
		SET_OOM_ERROR(*conn->error_info);
		goto end;
	}

	if (use_full_blown_auth_packet != TRUE) {
		change_auth_resp_packet = conn->protocol->m.get_change_auth_response_packet(conn->protocol, FALSE);
		if (!change_auth_resp_packet) {
			SET_OOM_ERROR(*conn->error_info);
			goto end;
		}

		change_auth_resp_packet->auth_data = auth_plugin_data;
		change_auth_resp_packet->auth_data_len = auth_plugin_data_len;

		if (!PACKET_WRITE(change_auth_resp_packet, conn)) {
			CONN_SET_STATE(conn, CONN_QUIT_SENT);
			SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
			goto end;
		}
	} else {
		auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE);

		if (!auth_packet) {
			SET_OOM_ERROR(*conn->error_info);
			goto end;
		}

		auth_packet->is_change_user_packet = TRUE;
		auth_packet->user		= user;
		auth_packet->db			= db;
		auth_packet->db_len		= db_len;
		auth_packet->silent		= silent;

		auth_packet->auth_data = auth_plugin_data;
		auth_packet->auth_data_len = auth_plugin_data_len;
		auth_packet->auth_plugin_name = auth_protocol;


		if (conn->m->get_server_version(conn) >= 50123) {
			auth_packet->charset_no	= conn->charset->nr;
		}

		if (!PACKET_WRITE(auth_packet, conn)) {
			CONN_SET_STATE(conn, CONN_QUIT_SENT);
				SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
			goto end;
		}
	}

	ret = PACKET_READ(chg_user_resp, conn);
	COPY_CLIENT_ERROR(*conn->error_info, chg_user_resp->error_info);

	if (0xFE == chg_user_resp->response_code) {
		ret = FAIL;
		if (!chg_user_resp->new_auth_protocol) {
			DBG_ERR(mysqlnd_old_passwd);
			SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
		} else {
			*switch_to_auth_protocol = mnd_pestrndup(chg_user_resp->new_auth_protocol, chg_user_resp->new_auth_protocol_len, FALSE);
			*switch_to_auth_protocol_len = chg_user_resp->new_auth_protocol_len;
			if (chg_user_resp->new_auth_protocol_data) {
				*switch_to_auth_protocol_data_len = chg_user_resp->new_auth_protocol_data_len;
				*switch_to_auth_protocol_data = mnd_emalloc(*switch_to_auth_protocol_data_len);
				memcpy(*switch_to_auth_protocol_data, chg_user_resp->new_auth_protocol_data, *switch_to_auth_protocol_data_len);
			} else {
				*switch_to_auth_protocol_data = NULL;
				*switch_to_auth_protocol_data_len = 0;
			}
		}
	}

	if (conn->error_info->error_no) {
		ret = FAIL;
		/*
		  COM_CHANGE_USER is broken in 5.1. At least in 5.1.15 and 5.1.14, 5.1.11 is immune.
		  bug#25371 mysql_change_user() triggers "packets out of sync"
		  When it gets fixed, there should be one more check here
		*/
		if (conn->m->get_server_version(conn) > 50113L &&conn->m->get_server_version(conn) < 50118L) {
			MYSQLND_PACKET_OK * redundant_error_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE);
			if (redundant_error_packet) {
				PACKET_READ(redundant_error_packet, conn);
				PACKET_FREE(redundant_error_packet);
				DBG_INF_FMT("Server is %u, buggy, sends two ERR messages", conn->m->get_server_version(conn));
			} else {
				SET_OOM_ERROR(*conn->error_info);
			}
		}
	}
	if (ret == PASS) {
		char * tmp = NULL;
		/* if we get conn->user as parameter and then we first free it, then estrndup it, we will crash */
		tmp = mnd_pestrndup(user, user_len, conn->persistent);
		if (conn->user) {
			mnd_pefree(conn->user, conn->persistent);
		}
		conn->user = tmp;

		tmp = mnd_pestrdup(passwd, conn->persistent);
		if (conn->passwd) {
			mnd_pefree(conn->passwd, conn->persistent);
		}
		conn->passwd = tmp;

		if (conn->last_message) {
			mnd_pefree(conn->last_message, conn->persistent);
			conn->last_message = NULL;
		}
		memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
		/* set charset for old servers */
		if (conn->m->get_server_version(conn) < 50123) {
			ret = conn->m->set_charset(conn, old_cs->name);
		}
	} else if (ret == FAIL && chg_user_resp->server_asked_323_auth == TRUE) {
		/* old authentication with new server  !*/
		DBG_ERR(mysqlnd_old_passwd);
		SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
	}
end:
	PACKET_FREE(change_auth_resp_packet);
	PACKET_FREE(auth_packet);
	PACKET_FREE(chg_user_resp);
	DBG_RETURN(ret);
}
Пример #9
0
/* {{{ mysqlnd_run_authentication */
enum_func_status
mysqlnd_run_authentication(
			MYSQLND_CONN_DATA * conn,
			const char * const user,
			const char * const passwd,
			const size_t passwd_len,
			const char * const db,
			const size_t db_len,
			const MYSQLND_STRING auth_plugin_data,
			const char * const auth_protocol,
			unsigned int charset_no,
			const MYSQLND_SESSION_OPTIONS * const session_options,
			zend_ulong mysql_flags,
			zend_bool silent,
			zend_bool is_change_user
			)
{
	enum_func_status ret = FAIL;
	zend_bool first_call = TRUE;

	char * switch_to_auth_protocol = NULL;
	size_t switch_to_auth_protocol_len = 0;
	char * requested_protocol = NULL;
	zend_uchar * plugin_data;
	size_t plugin_data_len;

	DBG_ENTER("mysqlnd_run_authentication");

	plugin_data_len = auth_plugin_data.l;
	plugin_data = mnd_emalloc(plugin_data_len + 1);
	if (!plugin_data) {
		goto end;
	}
	memcpy(plugin_data, auth_plugin_data.s, plugin_data_len);
	plugin_data[plugin_data_len] = '\0';

	requested_protocol = mnd_pestrdup(auth_protocol? auth_protocol : MYSQLND_DEFAULT_AUTH_PROTOCOL, FALSE);
	if (!requested_protocol) {
		goto end;
	}

	do {
		struct st_mysqlnd_authentication_plugin * auth_plugin = conn->m->fetch_auth_plugin_by_name(requested_protocol);

		if (!auth_plugin) {
			php_error_docref(NULL, E_WARNING, "The server requested authentication method unknown to the client [%s]", requested_protocol);
			SET_CLIENT_ERROR(conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "The server requested authentication method unknown to the client");
			goto end;
		}
		DBG_INF("plugin found");

		{
			zend_uchar * switch_to_auth_protocol_data = NULL;
			size_t switch_to_auth_protocol_data_len = 0;
			zend_uchar * scrambled_data = NULL;
			size_t scrambled_data_len = 0;

			switch_to_auth_protocol = NULL;
			switch_to_auth_protocol_len = 0;

			if (conn->authentication_plugin_data.s) {
				mnd_pefree(conn->authentication_plugin_data.s, conn->persistent);
				conn->authentication_plugin_data.s = NULL;
			}
			conn->authentication_plugin_data.l = plugin_data_len;
			conn->authentication_plugin_data.s = mnd_pemalloc(conn->authentication_plugin_data.l, conn->persistent);
			if (!conn->authentication_plugin_data.s) {
				SET_OOM_ERROR(conn->error_info);
				goto end;
			}
			memcpy(conn->authentication_plugin_data.s, plugin_data, plugin_data_len);

			DBG_INF_FMT("salt(%d)=[%.*s]", plugin_data_len, plugin_data_len, plugin_data);
			/* The data should be allocated with malloc() */
			scrambled_data =
				auth_plugin->methods.get_auth_data(NULL, &scrambled_data_len, conn, user, passwd, passwd_len,
												   plugin_data, plugin_data_len, session_options,
												   conn->protocol_frame_codec->data, mysql_flags);
			if (conn->error_info->error_no) {
				goto end;
			}
			if (FALSE == is_change_user) {
				ret = mysqlnd_auth_handshake(conn, user, passwd, passwd_len, db, db_len, session_options, mysql_flags,
											charset_no,
											first_call,
											requested_protocol,
											scrambled_data, scrambled_data_len,
											&switch_to_auth_protocol, &switch_to_auth_protocol_len,
											&switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
											);
			} else {
				ret = mysqlnd_auth_change_user(conn, user, strlen(user), passwd, passwd_len, db, db_len, silent,
											   first_call,
											   requested_protocol,
											   scrambled_data, scrambled_data_len,
											   &switch_to_auth_protocol, &switch_to_auth_protocol_len,
											   &switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
											  );
			}
			first_call = FALSE;
			free(scrambled_data);

			DBG_INF_FMT("switch_to_auth_protocol=%s", switch_to_auth_protocol? switch_to_auth_protocol:"n/a");
			if (requested_protocol && switch_to_auth_protocol) {
				mnd_efree(requested_protocol);
				requested_protocol = switch_to_auth_protocol;
			}

			if (plugin_data) {
				mnd_efree(plugin_data);
			}
			plugin_data_len = switch_to_auth_protocol_data_len;
			plugin_data = switch_to_auth_protocol_data;
		}
		DBG_INF_FMT("conn->error_info->error_no = %d", conn->error_info->error_no);
	} while (ret == FAIL && conn->error_info->error_no == 0 && switch_to_auth_protocol != NULL);

	if (ret == PASS) {
		DBG_INF_FMT("saving requested_protocol=%s", requested_protocol);
		conn->m->set_client_option(conn, MYSQLND_OPT_AUTH_PROTOCOL, requested_protocol);
	}
end:
	if (plugin_data) {
		mnd_efree(plugin_data);
	}
	if (requested_protocol) {
		mnd_efree(requested_protocol);
	}

	DBG_RETURN(ret);
}
Пример #10
0
/* {{{ mysqlnd_res_meta::read_metadata */
static enum_func_status
MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const meta, MYSQLND_CONN_DATA * conn)
{
	unsigned int i = 0;
	MYSQLND_PACKET_RES_FIELD * field_packet;

	DBG_ENTER("mysqlnd_res_meta::read_metadata");

	field_packet = conn->payload_decoder_factory->m.get_result_field_packet(conn->payload_decoder_factory, FALSE);
	if (!field_packet) {
		SET_OOM_ERROR(conn->error_info);
		DBG_RETURN(FAIL);
	}
	field_packet->persistent_alloc = meta->persistent;
	for (;i < meta->field_count; i++) {
		zend_ulong idx;

		if (meta->fields[i].root) {
			/* We re-read metadata for PS */
			mnd_pefree(meta->fields[i].root, meta->persistent);
			meta->fields[i].root = NULL;
		}

		field_packet->metadata = &(meta->fields[i]);
		if (FAIL == PACKET_READ(field_packet)) {
			PACKET_FREE(field_packet);
			DBG_RETURN(FAIL);
		}
		if (field_packet->error_info.error_no) {
			COPY_CLIENT_ERROR(conn->error_info, field_packet->error_info);
			/* Return back from CONN_QUERY_SENT */
			PACKET_FREE(field_packet);
			DBG_RETURN(FAIL);
		}

		if (mysqlnd_ps_fetch_functions[meta->fields[i].type].func == NULL) {
			DBG_ERR_FMT("Unknown type %u sent by the server.  Please send a report to the developers", meta->fields[i].type);
			php_error_docref(NULL, E_WARNING, "Unknown type %u sent by the server. Please send a report to the developers", meta->fields[i].type);
			PACKET_FREE(field_packet);
			DBG_RETURN(FAIL);
		}
		if (meta->fields[i].type == MYSQL_TYPE_BIT) {
			size_t field_len;
			DBG_INF("BIT");
			++meta->bit_fields_count;
			/* .length is in bits */
			field_len = meta->fields[i].length / 8;
			/*
			  If there is rest, add one byte :
			  8 bits = 1 byte but 9 bits = 2 bytes
			*/
			if (meta->fields[i].length % 8) {
				++field_len;
			}
			switch (field_len) {
				case 8:
				case 7:
				case 6:
				case 5:
					meta->bit_fields_total_len += 20;/* 21 digis, no sign*/
					break;
				case 4:
					meta->bit_fields_total_len += 10;/* 2 000 000 000*/
					break;
				case 3:
					meta->bit_fields_total_len += 8;/*  12 000 000*/
					break;
				case 2:
					meta->bit_fields_total_len += 5;/* 32 500 */
					break;
				case 1:
					meta->bit_fields_total_len += 3;/* 120 */
					break;
			}
		}

		/* For BC we have to check whether the key is numeric and use it like this */
		if ((meta->zend_hash_keys[i].is_numeric = ZEND_HANDLE_NUMERIC(field_packet->metadata->sname, idx))) {
			meta->zend_hash_keys[i].key = idx;
		}
	}
	PACKET_FREE(field_packet);

	DBG_RETURN(PASS);
}
Пример #11
0
/* {{{ mysqlnd_com_handshake_run */
static enum_func_status
mysqlnd_com_handshake_run(void *cmd)
{
	struct st_mysqlnd_protocol_com_handshake_command * command = (struct st_mysqlnd_protocol_com_handshake_command *) cmd;
	const char * user = command->context.user.s;

	const char * passwd = command->context.passwd.s;
	size_t passwd_len = command->context.passwd.l;

	const char * db = command->context.database.s;
	size_t db_len = command->context.database.l;

	size_t mysql_flags =  command->context.client_flags;

	MYSQLND_CONN_DATA * conn = command->context.conn;
	MYSQLND_PACKET_GREET * greet_packet;

	DBG_ENTER("mysqlnd_conn_data::connect_handshake");
	DBG_INF_FMT("stream=%p", conn->vio->data->m.get_stream(conn->vio));
	DBG_INF_FMT("[user=%s] [db=%s:%d] [flags=%llu]", user, db, db_len, mysql_flags);

	greet_packet = conn->payload_decoder_factory->m.get_greet_packet(conn->payload_decoder_factory, FALSE);
	if (!greet_packet) {
		SET_OOM_ERROR(conn->error_info);
		DBG_RETURN(FAIL); /* OOM */
	}

	if (FAIL == PACKET_READ(greet_packet)) {
		DBG_ERR("Error while reading greeting packet");
		php_error_docref(NULL, E_WARNING, "Error while reading greeting packet. PID=%d", getpid());
		goto err;
	} else if (greet_packet->error_no) {
		DBG_ERR_FMT("errorno=%u error=%s", greet_packet->error_no, greet_packet->error);
		SET_CLIENT_ERROR(conn->error_info, greet_packet->error_no, greet_packet->sqlstate, greet_packet->error);
		goto err;
	} else if (greet_packet->pre41) {
		DBG_ERR_FMT("Connecting to 3.22, 3.23 & 4.0 is not supported. Server is %-.32s", greet_packet->server_version);
		php_error_docref(NULL, E_WARNING, "Connecting to 3.22, 3.23 & 4.0 "
						" is not supported. Server is %-.32s", greet_packet->server_version);
		SET_CLIENT_ERROR(conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE,
						 "Connecting to 3.22, 3.23 & 4.0 servers is not supported");
		goto err;
	}

	conn->thread_id			= greet_packet->thread_id;
	conn->protocol_version	= greet_packet->protocol_version;
	conn->server_version	= mnd_pestrdup(greet_packet->server_version, conn->persistent);

	conn->greet_charset = mysqlnd_find_charset_nr(greet_packet->charset_no);
	if (!conn->greet_charset) {
		php_error_docref(NULL, E_WARNING,
			"Server sent charset (%d) unknown to the client. Please, report to the developers", greet_packet->charset_no);
		SET_CLIENT_ERROR(conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE,
			"Server sent charset unknown to the client. Please, report to the developers");
		goto err;
	}

	conn->server_capabilities 	= greet_packet->server_capabilities;

	if (FAIL == mysqlnd_connect_run_authentication(conn, user, passwd, db, db_len, (size_t) passwd_len,
												   greet_packet->authentication_plugin_data, greet_packet->auth_protocol,
												   greet_packet->charset_no, greet_packet->server_capabilities,
												   conn->options, mysql_flags))
	{
		goto err;
	}

	UPSERT_STATUS_RESET(conn->upsert_status);
	UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, greet_packet->server_status);

	PACKET_FREE(greet_packet);
	DBG_RETURN(PASS);
err:
	conn->server_capabilities = 0;
	PACKET_FREE(greet_packet);
	DBG_RETURN(FAIL);
}
Пример #12
0
/* {{{ mysqlnd_com_enable_ssl_run */
static enum_func_status
mysqlnd_com_enable_ssl_run(void *cmd)
{
	struct st_mysqlnd_protocol_com_enable_ssl_command * command = (struct st_mysqlnd_protocol_com_enable_ssl_command *) cmd;
	enum_func_status ret = FAIL;
	MYSQLND_CONN_DATA * conn = command->context.conn;
	MYSQLND_PACKET_AUTH * auth_packet;
	size_t client_capabilities = command->context.client_capabilities;
	size_t server_capabilities = command->context.server_capabilities;

	DBG_ENTER("mysqlnd_com_enable_ssl_run");
	DBG_INF_FMT("client_capability_flags=%lu", client_capabilities);
	DBG_INF_FMT("CLIENT_LONG_PASSWORD=	%d", client_capabilities & CLIENT_LONG_PASSWORD? 1:0);
	DBG_INF_FMT("CLIENT_FOUND_ROWS=		%d", client_capabilities & CLIENT_FOUND_ROWS? 1:0);
	DBG_INF_FMT("CLIENT_LONG_FLAG=		%d", client_capabilities & CLIENT_LONG_FLAG? 1:0);
	DBG_INF_FMT("CLIENT_NO_SCHEMA=		%d", client_capabilities & CLIENT_NO_SCHEMA? 1:0);
	DBG_INF_FMT("CLIENT_COMPRESS=		%d", client_capabilities & CLIENT_COMPRESS? 1:0);
	DBG_INF_FMT("CLIENT_ODBC=			%d", client_capabilities & CLIENT_ODBC? 1:0);
	DBG_INF_FMT("CLIENT_LOCAL_FILES=	%d", client_capabilities & CLIENT_LOCAL_FILES? 1:0);
	DBG_INF_FMT("CLIENT_IGNORE_SPACE=	%d", client_capabilities & CLIENT_IGNORE_SPACE? 1:0);
	DBG_INF_FMT("CLIENT_PROTOCOL_41=	%d", client_capabilities & CLIENT_PROTOCOL_41? 1:0);
	DBG_INF_FMT("CLIENT_INTERACTIVE=	%d", client_capabilities & CLIENT_INTERACTIVE? 1:0);
	DBG_INF_FMT("CLIENT_SSL=			%d", client_capabilities & CLIENT_SSL? 1:0);
	DBG_INF_FMT("CLIENT_IGNORE_SIGPIPE=	%d", client_capabilities & CLIENT_IGNORE_SIGPIPE? 1:0);
	DBG_INF_FMT("CLIENT_TRANSACTIONS=	%d", client_capabilities & CLIENT_TRANSACTIONS? 1:0);
	DBG_INF_FMT("CLIENT_RESERVED=		%d", client_capabilities & CLIENT_RESERVED? 1:0);
	DBG_INF_FMT("CLIENT_SECURE_CONNECTION=%d", client_capabilities & CLIENT_SECURE_CONNECTION? 1:0);
	DBG_INF_FMT("CLIENT_MULTI_STATEMENTS=%d", client_capabilities & CLIENT_MULTI_STATEMENTS? 1:0);
	DBG_INF_FMT("CLIENT_MULTI_RESULTS=	%d", client_capabilities & CLIENT_MULTI_RESULTS? 1:0);
	DBG_INF_FMT("CLIENT_PS_MULTI_RESULTS=%d", client_capabilities & CLIENT_PS_MULTI_RESULTS? 1:0);
	DBG_INF_FMT("CLIENT_CONNECT_ATTRS=	%d", client_capabilities & CLIENT_PLUGIN_AUTH? 1:0);
	DBG_INF_FMT("CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA=	%d", client_capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA? 1:0);
	DBG_INF_FMT("CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS=	%d", client_capabilities & CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS? 1:0);
	DBG_INF_FMT("CLIENT_SESSION_TRACK=		%d", client_capabilities & CLIENT_SESSION_TRACK? 1:0);
	DBG_INF_FMT("CLIENT_SSL_VERIFY_SERVER_CERT=	%d", client_capabilities & CLIENT_SSL_VERIFY_SERVER_CERT? 1:0);
	DBG_INF_FMT("CLIENT_REMEMBER_OPTIONS=		%d", client_capabilities & CLIENT_REMEMBER_OPTIONS? 1:0);

	auth_packet = conn->payload_decoder_factory->m.get_auth_packet(conn->payload_decoder_factory, FALSE);
	if (!auth_packet) {
		SET_OOM_ERROR(conn->error_info);
		goto end;
	}
	auth_packet->client_flags = client_capabilities;
	auth_packet->max_packet_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;

	auth_packet->charset_no	= command->context.charset_no;

#ifdef MYSQLND_SSL_SUPPORTED
	if (client_capabilities & CLIENT_SSL) {
		const zend_bool server_has_ssl = (server_capabilities & CLIENT_SSL)? TRUE:FALSE;
		if (server_has_ssl == FALSE) {
			goto close_conn;
		} else {
			enum mysqlnd_ssl_peer verify = client_capabilities & CLIENT_SSL_VERIFY_SERVER_CERT?
												MYSQLND_SSL_PEER_VERIFY:
												(client_capabilities & CLIENT_SSL_DONT_VERIFY_SERVER_CERT?
													MYSQLND_SSL_PEER_DONT_VERIFY:
													MYSQLND_SSL_PEER_DEFAULT);
			DBG_INF("Switching to SSL");
			if (!PACKET_WRITE(auth_packet)) {
				goto close_conn;
			}

			conn->vio->data->m.set_client_option(conn->vio, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (const char *) &verify);

			if (FAIL == conn->vio->data->m.enable_ssl(conn->vio)) {
				goto end;
			}
		}
	}
#else
	auth_packet->client_flags &= ~CLIENT_SSL;
	if (!PACKET_WRITE(auth_packet)) {
		goto close_conn;
	}
#endif
	ret = PASS;
end:
	PACKET_FREE(auth_packet);
	DBG_RETURN(ret);

close_conn:
	SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
	conn->m->send_close(conn);
	SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
	PACKET_FREE(auth_packet);
	DBG_RETURN(ret);
}
Пример #13
0
/* {{{ mysqlnd_stmt_execute_store_params */
static enum_func_status
mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar **p, size_t *buf_len )
{
	MYSQLND_STMT_DATA * stmt = s->data;
	zend_uchar * provided_buffer = *buf;
	size_t data_size = 0;
	zval *copies = NULL;/* if there are different types */
	enum_func_status ret = FAIL;
	int resend_types_next_time = 0;
	size_t null_byte_offset;

	DBG_ENTER("mysqlnd_stmt_execute_store_params");

	{
		unsigned int null_count = (stmt->param_count + 7) / 8;
		if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, null_count)) {
			SET_OOM_ERROR(*stmt->error_info);
			goto end;
		}
		/* put `null` bytes */
		null_byte_offset = *p - *buf;
		memset(*p, 0, null_count);
		*p += null_count;
	}

/* 1. Store type information */
	/*
	  check if need to send the types even if stmt->send_types_to_server is 0. This is because
	  if we send "i" (42) then the type will be int and the server will expect int. However, if next
	  time we try to send > LONG_MAX, the conversion to string will send a string and the server
	  won't expect it and interpret the value as 0. Thus we need to resend the types, if any such values
	  occur, and force resend for the next execution.
	*/
	if (FAIL == mysqlnd_stmt_execute_prepare_param_types(stmt, &copies, &resend_types_next_time)) {
		goto end;
	}

	int1store(*p, stmt->send_types_to_server);
	(*p)++;

	if (stmt->send_types_to_server) {
		if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, stmt->param_count * 2)) {
			SET_OOM_ERROR(*stmt->error_info);
			goto end;
		}
		mysqlnd_stmt_execute_store_types(stmt, copies, p);
	}

	stmt->send_types_to_server = resend_types_next_time;

/* 2. Store data */
	/* 2.1 Calculate how much space we need */
	if (FAIL == mysqlnd_stmt_execute_calculate_param_values_size(stmt, &copies, &data_size)) {
		goto end;
	}

	/* 2.2 Enlarge the buffer, if needed */
	if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, data_size)) {
		SET_OOM_ERROR(*stmt->error_info);
		goto end;
	}

	/* 2.3 Store the actual data */
	mysqlnd_stmt_execute_store_param_values(stmt, copies, *buf, p, null_byte_offset);

	ret = PASS;
end:
	mysqlnd_stmt_free_copies(stmt, copies);

	DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"******");
	DBG_RETURN(ret);
}
Пример #14
0
/* {{{ mysqlnd_stmt_execute_calculate_param_values_size */
static enum_func_status
mysqlnd_stmt_execute_calculate_param_values_size(MYSQLND_STMT_DATA * stmt, zval ** copies_param, size_t * data_size)
{
	unsigned int i;
	DBG_ENTER("mysqlnd_stmt_execute_calculate_param_values_size");
	for (i = 0; i < stmt->param_count; i++) {
		unsigned short is_longlong = 0;
		unsigned int j;
		zval *bind_var, *the_var = &stmt->param_bind[i].zv;

		bind_var = the_var;
		ZVAL_DEREF(the_var);
		if ((stmt->param_bind[i].type != MYSQL_TYPE_LONG_BLOB && Z_TYPE_P(the_var) == IS_NULL)) {
			continue;
		}

		if (Z_ISREF_P(bind_var)) {
			for (j = i + 1; j < stmt->param_count; j++) {
				if (Z_ISREF(stmt->param_bind[j].zv) && Z_REFVAL(stmt->param_bind[j].zv) == the_var) {
					/* Double binding of the same zval, make a copy */
					if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
						if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i)) {
							SET_OOM_ERROR(*stmt->error_info);
							goto end;
						}
					}
					break;
				}
			}
		}

		switch (stmt->param_bind[i].type) {
			case MYSQL_TYPE_DOUBLE:
				*data_size += 8;
				if (Z_TYPE_P(the_var) != IS_DOUBLE) {
					if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
						if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i)) {
							SET_OOM_ERROR(*stmt->error_info);
							goto end;
						}
					}
				}
				break;
			case MYSQL_TYPE_LONGLONG:
				is_longlong = 4;
				/* fall-through */
			case MYSQL_TYPE_LONG:
				{
					zval *tmp_data = (*copies_param && !Z_ISUNDEF((*copies_param)[i]))? &(*copies_param)[i]: the_var;
					if (Z_TYPE_P(tmp_data) == IS_STRING) {
						goto use_string;
					}
					convert_to_long_ex(tmp_data);
				}
				*data_size += 4 + is_longlong;
				break;
			case MYSQL_TYPE_LONG_BLOB:
				if (!(stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED)) {
					/*
					  User hasn't sent anything, we will send empty string.
					  Empty string has length of 0, encoded in 1 byte. No real
					  data will follows after it.
					*/
					(*data_size)++;
				}
				break;
			case MYSQL_TYPE_VAR_STRING:
use_string:
				*data_size += 8; /* max 8 bytes for size */
				if (Z_TYPE_P(the_var) != IS_STRING) {
					if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
						if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i)) {
							SET_OOM_ERROR(*stmt->error_info);
							goto end;
						}
					}
					the_var = &((*copies_param)[i]);
				}
				convert_to_string_ex(the_var);
				*data_size += Z_STRLEN_P(the_var);
				break;
		}
	}
	DBG_RETURN(PASS);
end:
	DBG_RETURN(FAIL);
}
Пример #15
0
/* {{{ mysqlnd_stmt_execute_store_params */
static enum_func_status
mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar **p,
								  size_t *buf_len, unsigned int null_byte_offset TSRMLS_DC)
{
	MYSQLND_STMT_DATA * stmt = s->data;
	unsigned int i = 0;
	zend_uchar * provided_buffer = *buf;
	size_t left = (*buf_len - (*p - *buf));
	size_t data_size = 0;
	zval **copies = NULL;/* if there are different types */
	enum_func_status ret = FAIL;
	int resend_types_next_time = 0;

	DBG_ENTER("mysqlnd_stmt_execute_store_params");

/* 1. Store type information */
	/*
	  check if need to send the types even if stmt->send_types_to_server is 0. This is because
	  if we send "i" (42) then the type will be int and the server will expect int. However, if next
	  time we try to send > LONG_MAX, the conversion to string will send a string and the server
	  won't expect it and interpret the value as 0. Thus we need to resend the types, if any such values
	  occur, and force resend for the next execution.
	*/
	for (i = 0; i < stmt->param_count; i++) {
		if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL &&
			(stmt->param_bind[i].type == MYSQL_TYPE_LONG || stmt->param_bind[i].type == MYSQL_TYPE_LONGLONG))
		{
			/* always copy the var, because we do many conversions */
			if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG &&
				PASS != mysqlnd_stmt_copy_it(&copies, stmt->param_bind[i].zv, stmt->param_count, i TSRMLS_CC))
			{
				SET_OOM_ERROR(stmt->error_info);
				goto end;
			}
			/*
			  if it doesn't fit in a long send it as a string.
			  Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
			*/
			if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG) {
				zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
				convert_to_double_ex(&tmp_data);
				if (Z_DVAL_P(tmp_data) > LONG_MAX || Z_DVAL_P(tmp_data) < LONG_MIN) {
					stmt->send_types_to_server = resend_types_next_time = 1;
				}
			}
		}
	}

	int1store(*p, stmt->send_types_to_server); 
	(*p)++;

	if (stmt->send_types_to_server) {
		/* 2 bytes per type, and leave 20 bytes for future use */
		if (left < ((stmt->param_count * 2) + 20)) {
			unsigned int offset = *p - *buf;
			zend_uchar *tmp_buf;
			*buf_len = offset + stmt->param_count * 2 + 20;
			tmp_buf = mnd_emalloc(*buf_len);
			if (!tmp_buf) {
				SET_OOM_ERROR(stmt->error_info);
				goto end;
			}
			memcpy(tmp_buf, *buf, offset);
			*buf = tmp_buf;

			/* Update our pos pointer */
			*p = *buf + offset;
		}
		for (i = 0; i < stmt->param_count; i++) {
			short current_type = stmt->param_bind[i].type;
			/* our types are not unsigned */
#if SIZEOF_LONG==8  
			if (current_type == MYSQL_TYPE_LONG) {
				current_type = MYSQL_TYPE_LONGLONG;
			}
#endif
			if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) {
				/*
				  if it doesn't fit in a long send it as a string.
				  Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
				*/
				if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG) {
					zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;

					convert_to_double_ex(&tmp_data);
					if (Z_DVAL_P(tmp_data) > LONG_MAX || Z_DVAL_P(tmp_data) < LONG_MIN) {
						convert_to_string_ex(&tmp_data);
						current_type = MYSQL_TYPE_VAR_STRING;
						/*
						  don't change stmt->param_bind[i].type to MYSQL_TYPE_VAR_STRING
						  we force convert_to_long_ex in all cases, thus the type will be right in the next switch.
						  if the type is however not long, then we will do a goto in the next switch.
						  We want to preserve the original bind type given by the user. Thus, we do these hacks.
						*/
					} else {
						convert_to_long_ex(&tmp_data);
					}
				}
			}
			int2store(*p, current_type);
			*p+= 2;
		}
	}
	stmt->send_types_to_server = resend_types_next_time;

/* 2. Store data */
	/* 2.1 Calculate how much space we need */
	for (i = 0; i < stmt->param_count; i++) {
		unsigned int j;
		zval *the_var = stmt->param_bind[i].zv;

		if (!the_var || (stmt->param_bind[i].type != MYSQL_TYPE_LONG_BLOB && Z_TYPE_P(the_var) == IS_NULL)) {
			continue;
		}
		for (j = i + 1; j < stmt->param_count; j++) {
			if (stmt->param_bind[j].zv == the_var) {
				/* Double binding of the same zval, make a copy */
				if (!copies || !copies[i]) {
					if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
						SET_OOM_ERROR(stmt->error_info);
						goto end;
					}
				}
				break; 
			}
		}

		switch (stmt->param_bind[i].type) {
			case MYSQL_TYPE_DOUBLE:
				data_size += 8;
				if (Z_TYPE_P(the_var) != IS_DOUBLE) {
					if (!copies || !copies[i]) {
						if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
							SET_OOM_ERROR(stmt->error_info);
							goto end;
						}
					}
				}
				break;
			case MYSQL_TYPE_LONGLONG:
				{
					zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
					if (Z_TYPE_P(tmp_data) == IS_STRING) {
						goto use_string;
					}
					convert_to_long_ex(&tmp_data);
				}
				data_size += 8;
				break;
			case MYSQL_TYPE_LONG:
				{
					zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
					if (Z_TYPE_P(tmp_data) == IS_STRING) {
						goto use_string;
					}
					convert_to_long_ex(&tmp_data);
				}
				data_size += 4;
				break;
			case MYSQL_TYPE_LONG_BLOB:
				if (!(stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED)) {
					/*
					  User hasn't sent anything, we will send empty string.
					  Empty string has length of 0, encoded in 1 byte. No real
					  data will follows after it.
					*/
					data_size++;
				}
				break;
			case MYSQL_TYPE_VAR_STRING:
use_string:
				data_size += 8; /* max 8 bytes for size */
#if MYSQLND_UNICODE
				if (Z_TYPE_P(the_var) != IS_STRING || Z_TYPE_P(the_var) == IS_UNICODE)
#else
				if (Z_TYPE_P(the_var) != IS_STRING)
#endif
				{
					if (!copies || !copies[i]) {
						if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
							SET_OOM_ERROR(stmt->error_info);
							goto end;
						}
					}
					the_var = copies[i];
#if MYSQLND_UNICODE
					if (Z_TYPE_P(the_var) == IS_UNICODE) {
						zval_unicode_to_string_ex(the_var, UG(utf8_conv) TSRMLS_CC);
					}
#endif
				}
				convert_to_string_ex(&the_var);
				data_size += Z_STRLEN_P(the_var);
				break;
		}
	}

	/* 2.2 Enlarge the buffer, if needed */
	left = (*buf_len - (*p - *buf));
	if (left < data_size) {
		unsigned int offset = *p - *buf;
		zend_uchar *tmp_buf;
		*buf_len = offset + data_size + 10; /* Allocate + 10 for safety */
		tmp_buf = mnd_emalloc(*buf_len);
		if (!tmp_buf) {
			SET_OOM_ERROR(stmt->error_info);
			goto end;
		}
		memcpy(tmp_buf, *buf, offset);
		/*
		  When too many columns the buffer provided to the function might not be sufficient.
		  In this case new buffer has been allocated above. When we allocate a buffer and then
		  allocate a bigger one here, we should free the first one.
		*/
		if (*buf != provided_buffer) {
			mnd_efree(*buf);
		}
		*buf = tmp_buf;
		/* Update our pos pointer */
		*p = *buf + offset;
	}

	/* 2.3 Store the actual data */
	for (i = 0; i < stmt->param_count; i++) {
		zval *data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
		/* Handle long data */
		if (stmt->param_bind[i].zv && Z_TYPE_P(data) == IS_NULL) {
			(*buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
		} else {
			switch (stmt->param_bind[i].type) {
				case MYSQL_TYPE_DOUBLE:
					convert_to_double_ex(&data);
					float8store(*p, Z_DVAL_P(data));
					(*p) += 8;
					break;
				case MYSQL_TYPE_LONGLONG:
					if (Z_TYPE_P(data) == IS_STRING) {
						goto send_string;
					}
					/* data has alreade been converted to long */
					int8store(*p, Z_LVAL_P(data));
					(*p) += 8;
					break;
				case MYSQL_TYPE_LONG:
					if (Z_TYPE_P(data) == IS_STRING) {
						goto send_string;
					}
					/* data has alreade been converted to long */
					int4store(*p, Z_LVAL_P(data));
					(*p) += 4;
					break;
				case MYSQL_TYPE_LONG_BLOB:
					if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) {
						stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
					} else {
						/* send_long_data() not called, send empty string */
						*p = php_mysqlnd_net_store_length(*p, 0);
					}
					break;
				case MYSQL_TYPE_VAR_STRING:
send_string:
					{
						unsigned int len = Z_STRLEN_P(data);
						/* to is after p. The latter hasn't been moved */
						*p = php_mysqlnd_net_store_length(*p, len);
						memcpy(*p, Z_STRVAL_P(data), len);
						(*p) += len;
					}
					break;
				default:
					/* Won't happen, but set to NULL */
					(*buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
					break;
			}
		}
	}
	ret = PASS;
end:
	if (copies) {
		for (i = 0; i < stmt->param_count; i++) {
			if (copies[i]) {
				zval_ptr_dtor(&copies[i]);
			}
		}
		mnd_efree(copies);
	}

	DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"******");
	DBG_RETURN(ret);
}