diff options
| -rw-r--r-- | ext/mysqlnd/mysqlnd.c | 93 | ||||
| -rw-r--r-- | ext/mysqlnd/mysqlnd.h | 1 | ||||
| -rw-r--r-- | ext/mysqlnd/mysqlnd_auth.c | 5 | ||||
| -rw-r--r-- | ext/mysqlnd/mysqlnd_enum_n_def.h | 3 | ||||
| -rw-r--r-- | ext/mysqlnd/mysqlnd_libmysql_compat.h | 3 | ||||
| -rw-r--r-- | ext/mysqlnd/mysqlnd_structs.h | 6 | ||||
| -rw-r--r-- | ext/mysqlnd/mysqlnd_wireprotocol.c | 66 | ||||
| -rw-r--r-- | ext/mysqlnd/mysqlnd_wireprotocol.h | 2 | 
8 files changed, 171 insertions, 8 deletions
| diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index ebc4a774a6..1023b3e5f4 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -95,6 +95,11 @@ MYSQLND_METHOD(mysqlnd_conn_data, free_options)(MYSQLND_CONN_DATA * conn TSRMLS_  		mnd_pefree(conn->options->cfg_section, pers);  		conn->options->cfg_section = NULL;  	} +	if (conn->options->connect_attr) { +		zend_hash_destroy(conn->options->connect_attr); +		mnd_pefree(conn->options->connect_attr, pers); +		conn->options->connect_attr = NULL; +	}  }  /* }}} */ @@ -797,13 +802,14 @@ MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake)(MYSQLND_CONN_DATA * conn,  		goto err;  	} +	conn->client_flag			= mysql_flags; +	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, conn->options, mysql_flags TSRMLS_CC))  	{  		goto err;  	} -	conn->client_flag			= mysql_flags; -	conn->server_capabilities 	= greet_packet->server_capabilities;  	conn->upsert_status->warning_count = 0;  	conn->upsert_status->server_status = greet_packet->server_status;  	conn->upsert_status->affected_rows = 0; @@ -811,6 +817,8 @@ MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake)(MYSQLND_CONN_DATA * conn,  	PACKET_FREE(greet_packet);  	DBG_RETURN(PASS);  err: +	conn->client_flag = 0; +	conn->server_capabilities = 0;  	PACKET_FREE(greet_packet);  	DBG_RETURN(FAIL);  } @@ -1086,6 +1094,7 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn_handle,  	DBG_ENTER("mysqlnd_conn::connect");  	if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) { +		mysqlnd_options4(conn_handle, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_name", "mysqlnd");  		ret = conn->m->connect(conn, host, user, passwd, passwd_len, db, db_len, port, socket_or_pipe, mysql_flags TSRMLS_CC);  		conn->m->local_tx_end(conn, this_func, FAIL TSRMLS_CC); @@ -2375,6 +2384,19 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const c  				conn->options->flags &= ~CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;  			}  			break; +		case MYSQL_OPT_CONNECT_ATTR_RESET: +			if (conn->options->connect_attr) { +				DBG_INF_FMT("Before reset %d attribute(s)", zend_hash_num_elements(conn->options->connect_attr)); +				zend_hash_clean(conn->options->connect_attr); +			} +			break; +		case MYSQL_OPT_CONNECT_ATTR_DELETE: +			if (conn->options->connect_attr && value) { +				DBG_INF_FMT("Before delete %d attribute(s)", zend_hash_num_elements(conn->options->connect_attr)); +				zend_hash_del(conn->options->connect_attr, value, strlen(value)); +				DBG_INF_FMT("%d left", zend_hash_num_elements(conn->options->connect_attr)); +			} +			break;  #ifdef WHEN_SUPPORTED_BY_MYSQLI  		case MYSQL_SHARED_MEMORY_BASE_NAME:  		case MYSQL_OPT_USE_RESULT: @@ -2395,6 +2417,69 @@ end:  /* }}} */ +/* {{{ connect_attr_item_dtor */ +static void +connect_attr_item_dtor(void * pDest) +{ +#ifdef ZTS +	TSRMLS_FETCH(); +#endif +	DBG_ENTER("connect_attr_item_dtor"); +	mnd_pefree(*(char **) pDest, 1); +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysqlnd_conn_data::set_client_option_2d */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d)(MYSQLND_CONN_DATA * const conn, +														enum mysqlnd_option option, +														const char * const key, +														const char * const value +														TSRMLS_DC) +{ +	size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_client_option_2d); +	enum_func_status ret = PASS; +	DBG_ENTER("mysqlnd_conn_data::set_client_option_2d"); +	DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option); + +	if (PASS != conn->m->local_tx_start(conn, this_func TSRMLS_CC)) { +		goto end; +	} +	switch (option) { +		case MYSQL_OPT_CONNECT_ATTR_ADD: +			if (!conn->options->connect_attr) { +				DBG_INF("Initializing connect_attr hash"); +				conn->options->connect_attr = mnd_pemalloc(sizeof(HashTable), conn->persistent); +				if (!conn->options->connect_attr) { +					goto oom; +				} +				zend_hash_init(conn->options->connect_attr, 0, NULL, NULL, conn->persistent); +			} +			DBG_INF_FMT("Adding [%s][%s]", key, value); +			{ +				const char * copyv = mnd_pestrdup(value, 1); +				if (!copyv) { +					goto oom; +				} +				zend_hash_update(conn->options->connect_attr, key, strlen(key), ©v, sizeof(char *), NULL); +			} +			break; +		default: +			ret = FAIL; +	} +	conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);	 +	DBG_RETURN(ret); +oom: +	SET_OOM_ERROR(*conn->error_info); +	conn->m->local_tx_end(conn, this_func, FAIL TSRMLS_CC);	 +end: +	DBG_RETURN(FAIL); +} +/* }}} */ + +  /* {{{ mysqlnd_conn_data::use_result */  static MYSQLND_RES *  MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn TSRMLS_DC) @@ -2662,7 +2747,9 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)  	MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags),  	MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake),  	MYSQLND_METHOD(mysqlnd_conn_data, simple_command_send_request), -	MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name)	 +	MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name), + +	MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d)  MYSQLND_CLASS_METHODS_END; diff --git a/ext/mysqlnd/mysqlnd.h b/ext/mysqlnd/mysqlnd.h index 7e02126f3b..bf0f70b405 100644 --- a/ext/mysqlnd/mysqlnd.h +++ b/ext/mysqlnd/mysqlnd.h @@ -207,6 +207,7 @@ PHPAPI void mysqlnd_set_local_infile_handler(MYSQLND_CONN_DATA * const conn, con  #define mysqlnd_set_character_set(conn, cs)	((conn)->data)->m->set_charset((conn)->data, (cs) TSRMLS_CC)  #define mysqlnd_stat(conn, msg, msg_len)	((conn)->data)->m->get_server_statistics(((conn)->data), (msg), (msg_len) TSRMLS_CC)  #define mysqlnd_options(conn, opt, value)	((conn)->data)->m->set_client_option((conn)->data, (opt), (value) TSRMLS_CC) +#define mysqlnd_options4(conn, opt, k, v)	((conn)->data)->m->set_client_option_2d((conn)->data, (opt), (k), (v) TSRMLS_CC)  #define mysqlnd_set_server_option(conn, op)	((conn)->data)->m->set_server_option((conn)->data, (op) TSRMLS_CC)  /* Escaping */ diff --git a/ext/mysqlnd/mysqlnd_auth.c b/ext/mysqlnd/mysqlnd_auth.c index a3b4f36a2e..8611d99864 100644 --- a/ext/mysqlnd/mysqlnd_auth.c +++ b/ext/mysqlnd/mysqlnd_auth.c @@ -28,7 +28,6 @@  #include "mysqlnd_charset.h"  #include "mysqlnd_debug.h" -  /* {{{ mysqlnd_auth_handshake */  enum_func_status  mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn, @@ -99,6 +98,10 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,  		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; diff --git a/ext/mysqlnd/mysqlnd_enum_n_def.h b/ext/mysqlnd/mysqlnd_enum_n_def.h index abaaf1f43e..cf5b02728b 100644 --- a/ext/mysqlnd/mysqlnd_enum_n_def.h +++ b/ext/mysqlnd/mysqlnd_enum_n_def.h @@ -168,6 +168,9 @@ typedef enum mysqlnd_option  	MYSQL_OPT_SSL_VERIFY_SERVER_CERT,  	MYSQL_PLUGIN_DIR,  	MYSQL_DEFAULT_AUTH, +	MYSQL_OPT_CONNECT_ATTR_RESET, +	MYSQL_OPT_CONNECT_ATTR_ADD, +	MYSQL_OPT_CONNECT_ATTR_DELETE,  	MYSQL_SERVER_PUBLIC_KEY,  	MYSQL_ENABLE_CLEARTEXT_PLUGIN,  	MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS, diff --git a/ext/mysqlnd/mysqlnd_libmysql_compat.h b/ext/mysqlnd/mysqlnd_libmysql_compat.h index c967fb3a9f..e3ab9eefa9 100644 --- a/ext/mysqlnd/mysqlnd_libmysql_compat.h +++ b/ext/mysqlnd/mysqlnd_libmysql_compat.h @@ -106,7 +106,8 @@  #define mysql_stmt_more_results(s)		mysqlnd_stmt_more_results((s))  #define mysql_thread_safe()				mysqlnd_thread_safe()  #define mysql_info(r)					mysqlnd_info((r)) -#define mysql_options(r,a,b)			mysqlnd_options((r), (a), (b)) +#define mysql_options(c,a,v)			mysqlnd_options((c), (a), (v)) +#define mysql_options4(c,a,k,v)			mysqlnd_options4((c), (a), (k), (v))  #define mysql_stmt_init(r)				mysqlnd_stmt_init((r))  #define mysql_free_result(r)			mysqlnd_free_result((r), FALSE)  #define mysql_store_result(r)			mysqlnd_store_result((r)) diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h index 6302c81c6a..ecb1d89216 100644 --- a/ext/mysqlnd/mysqlnd_structs.h +++ b/ext/mysqlnd/mysqlnd_structs.h @@ -172,7 +172,7 @@ typedef struct st_mysqlnd_options  	  The ABI will be broken and the methods structure will be somewhere else  	  in the memory which can crash external code. Feel free to reuse these.  	*/ -	char		* unused2; +	HashTable	* connect_attr;  	char		* unused3;  	char		* unused4;  	char		* unused5; @@ -489,6 +489,8 @@ typedef enum_func_status	(*func_mysqlnd_conn_data__connect_handshake)(MYSQLND_CO  typedef enum_func_status	(*func_mysqlnd_conn_data__simple_command_send_request)(MYSQLND_CONN_DATA * conn, enum php_mysqlnd_server_command command, const zend_uchar * const arg, size_t arg_len, zend_bool silent, zend_bool ignore_upsert_status TSRMLS_DC);  typedef struct st_mysqlnd_authentication_plugin * (*func_mysqlnd_conn_data__fetch_auth_plugin_by_name)(const char * const requested_protocol TSRMLS_DC); +typedef enum_func_status	(*func_mysqlnd_conn_data__set_client_option_2d)(MYSQLND_CONN_DATA * const conn, enum mysqlnd_option option, const char * const key, const char * const value TSRMLS_DC); +  struct st_mysqlnd_conn_data_methods  {  	func_mysqlnd_conn_data__init init; @@ -573,6 +575,8 @@ struct st_mysqlnd_conn_data_methods  	func_mysqlnd_conn_data__connect_handshake connect_handshake;  	func_mysqlnd_conn_data__simple_command_send_request simple_command_send_request;  	func_mysqlnd_conn_data__fetch_auth_plugin_by_name fetch_auth_plugin_by_name; + +	func_mysqlnd_conn_data__set_client_option_2d set_client_option_2d;  }; diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c index 7c3bf1395f..669970789b 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.c +++ b/ext/mysqlnd/mysqlnd_wireprotocol.c @@ -212,6 +212,24 @@ php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length)  /* }}} */ +/* {{{ php_mysqlnd_net_store_length_size */ +size_t  +php_mysqlnd_net_store_length_size(uint64_t length) +{ +	if (length < (uint64_t) L64(251)) { +		return 1; +	} +	if (length < (uint64_t) L64(65536)) { +		return 3; +	} +	if (length < (uint64_t) L64(16777216)) { +		return 4; +	} +	return 8; +} +/* }}} */ + +  /* {{{ php_mysqlnd_read_error_from_line */  static enum_func_status  php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_len, @@ -459,7 +477,7 @@ void php_mysqlnd_greet_free_mem(void * _packet, zend_bool stack_allocation TSRML  /* }}} */ -#define AUTH_WRITE_BUFFER_LEN (MYSQLND_HEADER_SIZE + MYSQLND_MAX_ALLOWED_USER_LEN + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 1024) +#define AUTH_WRITE_BUFFER_LEN (MYSQLND_HEADER_SIZE + MYSQLND_MAX_ALLOWED_USER_LEN + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 4096)  /* {{{ php_mysqlnd_auth_write */  static @@ -540,6 +558,52 @@ size_t php_mysqlnd_auth_write(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC  			p+= len;  			*p++= '\0';  		} + +		if (packet->connect_attr && zend_hash_num_elements(packet->connect_attr)) { +			HashPosition pos_value; +			const char ** entry_value; +			size_t ca_payload_len = 0; +			zend_hash_internal_pointer_reset_ex(packet->connect_attr, &pos_value); +			while (SUCCESS == zend_hash_get_current_data_ex(packet->connect_attr, (void **)&entry_value, &pos_value)) { +				char *s_key; +				unsigned int s_len; +				unsigned long num_key; +				size_t value_len = strlen(*entry_value); +				 +				if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(packet->connect_attr, &s_key, &s_len, &num_key, 0, &pos_value)) { +					ca_payload_len += php_mysqlnd_net_store_length_size(s_len); +					ca_payload_len += s_len; +					ca_payload_len += php_mysqlnd_net_store_length_size(value_len); +					ca_payload_len += value_len; +				} +				zend_hash_move_forward_ex(conn->options->connect_attr, &pos_value); +			} + +			if ((sizeof(buffer) - (p - buffer)) >= (ca_payload_len + php_mysqlnd_net_store_length_size(ca_payload_len))) { +				p = php_mysqlnd_net_store_length(p, ca_payload_len); + +				zend_hash_internal_pointer_reset_ex(packet->connect_attr, &pos_value); +				while (SUCCESS == zend_hash_get_current_data_ex(packet->connect_attr, (void **)&entry_value, &pos_value)) { +					char *s_key; +					unsigned int s_len; +					unsigned long num_key; +					size_t value_len = strlen(*entry_value); +					if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(packet->connect_attr, &s_key, &s_len, &num_key, 0, &pos_value)) { +						/* copy key */ +						p = php_mysqlnd_net_store_length(p, s_len); +						memcpy(p, s_key, s_len); +						p+= s_len; +						/* copy value */ +						p = php_mysqlnd_net_store_length(p, value_len); +						memcpy(p, *entry_value, value_len); +						p+= value_len; +					} +					zend_hash_move_forward_ex(conn->options->connect_attr, &pos_value); +				} +			} else { +				/* cannot put the data - skip */ +			} +		}  	}  	if (packet->is_change_user_packet) {  		if (PASS != conn->m->simple_command(conn, COM_CHANGE_USER, buffer + MYSQLND_HEADER_SIZE, p - buffer - MYSQLND_HEADER_SIZE, diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.h b/ext/mysqlnd/mysqlnd_wireprotocol.h index e9c9024d04..26dd4c65ae 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.h +++ b/ext/mysqlnd/mysqlnd_wireprotocol.h @@ -103,7 +103,7 @@ typedef struct st_mysqlnd_packet_auth {  	zend_bool	send_auth_data;  	zend_bool	is_change_user_packet;  	zend_bool	silent; - +	HashTable	*connect_attr;  } MYSQLND_PACKET_AUTH;  /* Auth response packet */ | 
