Esempio n. 1
0
/* {{{ mysqlnd_mempool_get_chunk */
static
MYSQLND_MEMORY_POOL_CHUNK * mysqlnd_mempool_get_chunk(MYSQLND_MEMORY_POOL * pool, unsigned int size)
{
	MYSQLND_MEMORY_POOL_CHUNK *chunk = NULL;
	DBG_ENTER("mysqlnd_mempool_get_chunk");

	chunk = mnd_emalloc(sizeof(MYSQLND_MEMORY_POOL_CHUNK));
	if (chunk) {
		chunk->size = size;
		/*
		  Should not go over MYSQLND_MAX_PACKET_SIZE, since we
		  expect non-arena memory in mysqlnd_wireprotocol.c . We
		  realloc the non-arena memory.
		*/
		if (size > pool->free_size) {
			chunk->from_pool = FALSE;
			chunk->ptr = mnd_emalloc(size);
			if (!chunk->ptr) {
				pool->free_chunk(pool, chunk);
				chunk = NULL;
			}
		} else {
			chunk->from_pool = TRUE;
			chunk->ptr = pool->arena + (pool->arena_size - pool->free_size);
			/* Last step, update free_size */
			pool->free_size -= size;
		}
	}
	DBG_RETURN(chunk);
}
Esempio n. 2
0
/* {{{ mysqlnd_mempool_resize_chunk */
static enum_func_status
mysqlnd_mempool_resize_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk, unsigned int size)
{
	DBG_ENTER("mysqlnd_mempool_resize_chunk");
	if (chunk->from_pool) {
		MYSQLND_MEMORY_POOL * pool = chunk->pool;
		/* Try to back-off and guess if this is the last block allocated */
		if (chunk->ptr == (pool->arena + (pool->arena_size - pool->free_size - chunk->size))) {
			/*
				This was the last allocation. Lucky us, we can free
				a bit of memory from the pool. Next time we will return from the same ptr.
			*/
			if ((chunk->size + pool->free_size) < size) {
				zend_uchar *new_ptr;
				new_ptr = mnd_emalloc(size);
				if (!new_ptr) {
					DBG_RETURN(FAIL);
				}
				memcpy(new_ptr, chunk->ptr, chunk->size);
				chunk->ptr = new_ptr;
				pool->free_size += chunk->size;
				chunk->size = size;
				chunk->pool = NULL; /* now we have no pool memory */
				pool->refcount--;
			} else {
				/* If the chunk is > than asked size then free_memory increases, otherwise decreases*/
				pool->free_size += (chunk->size - size);
			}
		} else {
			/* Not last chunk, if the user asks for less, give it to him */
			if (chunk->size >= size) {
				; /* nop */
			} else {
				zend_uchar *new_ptr;
				new_ptr = mnd_emalloc(size);
				if (!new_ptr) {
					DBG_RETURN(FAIL);
				}
				memcpy(new_ptr, chunk->ptr, chunk->size);
				chunk->ptr = new_ptr;
				chunk->size = size;
				chunk->pool = NULL; /* now we have non-pool memory */
				pool->refcount--;
			}
		}
	} else {
		zend_uchar *new_ptr = mnd_erealloc(chunk->ptr, size);
		if (!new_ptr) {
			DBG_RETURN(FAIL);
		}
		chunk->ptr = new_ptr;
	}
	DBG_RETURN(PASS);
}
Esempio n. 3
0
/* {{{ mysqlnd_create_read_buffer */
PHPAPI MYSQLND_READ_BUFFER *
mysqlnd_create_read_buffer(const size_t count)
{
	MYSQLND_READ_BUFFER * ret = mnd_emalloc(sizeof(MYSQLND_READ_BUFFER));
	DBG_ENTER("mysqlnd_create_read_buffer");
	ret->is_empty = mysqlnd_read_buffer_is_empty;
	ret->read = mysqlnd_read_buffer_read;
	ret->bytes_left = mysqlnd_read_buffer_bytes_left;
	ret->free_buffer = mysqlnd_read_buffer_free;
	ret->data = mnd_emalloc(count);
	ret->size = ret->len = count;
	ret->offset = 0;
	DBG_RETURN(ret);
}
Esempio n. 4
0
/* {{{ mysqlnd_arena_alloc */
static zend_always_inline void* mysqlnd_arena_alloc(zend_arena **arena_ptr, size_t size)
{
	zend_arena *arena = *arena_ptr;
	char *ptr = arena->ptr;

	size = ZEND_MM_ALIGNED_SIZE(size);

	if (EXPECTED(size <= (size_t)(arena->end - ptr))) {
		arena->ptr = ptr + size;
	} else {
		size_t arena_size =
			UNEXPECTED((size + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena))) > (size_t)(arena->end - (char*) arena)) ?
				(size + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena))) :
				(size_t)(arena->end - (char*) arena);
		zend_arena *new_arena = (zend_arena*)mnd_emalloc(arena_size);

		ptr = (char*) new_arena + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena));
		new_arena->ptr = (char*) new_arena + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena)) + size;
		new_arena->end = (char*) new_arena + arena_size;
		new_arena->prev = arena;
		*arena_ptr = new_arena;
	}

	return (void*) ptr;
}
Esempio n. 5
0
/* {{{ mysqlnd_arena_create */
static zend_always_inline zend_arena* mysqlnd_arena_create(size_t size)
{
	zend_arena *arena = (zend_arena*)mnd_emalloc(size);

	arena->ptr = (char*) arena + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena));
	arena->end = (char*) arena + size;
	arena->prev = NULL;
	return arena;
}
Esempio n. 6
0
/* {{{ mysqlnd_net::read_compressed_packet_from_stream_and_fill_read_buffer */
static enum_func_status
MYSQLND_METHOD(mysqlnd_net, read_compressed_packet_from_stream_and_fill_read_buffer)
		(MYSQLND_NET * net, size_t net_payload_size, MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info)
{
	size_t decompressed_size;
	enum_func_status retval = PASS;
	zend_uchar * compressed_data = NULL;
	zend_uchar comp_header[COMPRESSED_HEADER_SIZE];
	DBG_ENTER("mysqlnd_net::read_compressed_packet_from_stream_and_fill_read_buffer");

	/* Read the compressed header */
	if (FAIL == net->data->m.network_read_ex(net, comp_header, COMPRESSED_HEADER_SIZE, conn_stats, error_info)) {
		DBG_RETURN(FAIL);
	}
	decompressed_size = uint3korr(comp_header);

	/* When decompressed_size is 0, then the data is not compressed, and we have wasted 3 bytes */
	/* we need to decompress the data */

	if (decompressed_size) {
		compressed_data = mnd_emalloc(net_payload_size);
		if (FAIL == net->data->m.network_read_ex(net, compressed_data, net_payload_size, conn_stats, error_info)) {
			retval = FAIL;
			goto end;
		}
		net->uncompressed_data = mysqlnd_create_read_buffer(decompressed_size);
		retval = net->data->m.decode(net->uncompressed_data->data, decompressed_size, compressed_data, net_payload_size);
		if (FAIL == retval) {
			goto end;
		}
	} else {
		DBG_INF_FMT("The server decided not to compress the data. Our job is easy. Copying %u bytes", net_payload_size);
		net->uncompressed_data = mysqlnd_create_read_buffer(net_payload_size);
		if (FAIL == net->data->m.network_read_ex(net, net->uncompressed_data->data, net_payload_size, conn_stats, error_info)) {
			retval = FAIL;
			goto end;
		}
	}
end:
	if (compressed_data) {
		mnd_efree(compressed_data);
	}
	DBG_RETURN(retval);
}
Esempio n. 7
0
/* {{{ mysqlnd_mempool_create */
PHPAPI MYSQLND_MEMORY_POOL *
mysqlnd_mempool_create(size_t arena_size)
{
	/* We calloc, because we free(). We don't mnd_calloc()  for a reason. */
	MYSQLND_MEMORY_POOL * ret = mnd_ecalloc(1, sizeof(MYSQLND_MEMORY_POOL));
	DBG_ENTER("mysqlnd_mempool_create");
	if (ret) {
		ret->get_chunk = mysqlnd_mempool_get_chunk;
		ret->free_size = ret->arena_size = arena_size ? arena_size : 0;
		ret->refcount = 0;
		/* OOM ? */
		ret->arena = mnd_emalloc(ret->arena_size);
		if (!ret->arena) {
			mysqlnd_mempool_destroy(ret);
			ret = NULL;
		}
	}
	DBG_RETURN(ret);
}
Esempio n. 8
0
/* {{{ mysqlnd_stmt_execute_check_n_enlarge_buffer */
static enum_func_status
mysqlnd_stmt_execute_check_n_enlarge_buffer(zend_uchar **buf, zend_uchar **p, size_t * buf_len, zend_uchar * const provided_buffer, size_t needed_bytes)
{
	const size_t overalloc = 5;
	size_t left = (*buf_len - (*p - *buf));

	if (left < (needed_bytes + overalloc)) {
		size_t offset = *p - *buf;
		zend_uchar *tmp_buf;
		*buf_len = offset + needed_bytes + overalloc;
		tmp_buf = mnd_emalloc(*buf_len);
		if (!tmp_buf) {
			return FAIL;
		}
		memcpy(tmp_buf, *buf, offset);
		if (*buf != provided_buffer) {
			mnd_efree(*buf);
		}
		*buf = tmp_buf;
		/* Update our pos pointer */
		*p = *buf + offset;
	}
	return PASS;
}
Esempio n. 9
0
/* {{{ mysqlnd_stmt_execute_store_params */
static void
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;
	size_t left = (*buf_len - (*p - *buf));
	size_t data_size = 0;
	zval **copies = NULL;/* if there are different types */

/* 1. Store type information */
	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);
			memcpy(tmp_buf, *buf, offset);
			*buf = tmp_buf;
			
			/* Update our pos pointer */
			*p = *buf + offset;
		}
		for (i = 0; i < stmt->param_count; i++) {
			/* our types are not unsigned */
#if SIZEOF_LONG==8  
			if (stmt->param_bind[i].type == MYSQL_TYPE_LONG) {
				stmt->param_bind[i].type = MYSQL_TYPE_LONGLONG;
			}
#endif
			int2store(*p, stmt->param_bind[i].type);
			*p+= 2;
		}
	}

/* 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 */
				mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC);
				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]) {
						mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC);
					}
				}
				break;
#if SIZEOF_LONG==8  
			case MYSQL_TYPE_LONGLONG:
				data_size += 8;
#elif SIZEOF_LONG==4
			case MYSQL_TYPE_LONG:
				data_size += 4;
#else
#error "Should not happen"
#endif
				if (Z_TYPE_P(the_var) != IS_LONG) {
					if (!copies || !copies[i]) {
						mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC);
					}
				}
				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:
				data_size += 8; /* max 8 bytes for size */
#if PHP_MAJOR_VERSION < 6
				if (Z_TYPE_P(the_var) != IS_STRING)
#elif PHP_MAJOR_VERSION >= 6
				if (Z_TYPE_P(the_var) != IS_STRING || Z_TYPE_P(the_var) == IS_UNICODE)
#endif
				{
					if (!copies || !copies[i]) {
						mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC);
					}
					the_var = copies[i];
#if PHP_MAJOR_VERSION >= 6
					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);
		memcpy(tmp_buf, *buf, offset);
		*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;
#if SIZEOF_LONG==8  
				case MYSQL_TYPE_LONGLONG:
					convert_to_long_ex(&data);
					int8store(*p, Z_LVAL_P(data));
					(*p) += 8;
					break;
#elif SIZEOF_LONG==4
				case MYSQL_TYPE_LONG:
					convert_to_long_ex(&data);
					int4store(*p, Z_LVAL_P(data));
					(*p) += 4;
					break;
#else
#error "Should not happen"
#endif
				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:{
						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;
			}
		}
	}
	if (copies) {
		for (i = 0; i < stmt->param_count; i++) {
			if (copies[i]) {
				zval_ptr_dtor(&copies[i]);
			}
		}
		mnd_efree(copies);	
	}
}
Esempio n. 10
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);
}
Esempio n. 11
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);
}
Esempio n. 12
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);
}
Esempio n. 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, 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);
}
Esempio n. 14
0
/*
  IMPORTANT : It's expected that buffer has place in the beginning for MYSQLND_HEADER_SIZE !!!!
			  This is done for performance reasons in the caller of this function.
			  Otherwise we will have to do send two TCP packets, or do new alloc and memcpy.
			  Neither are quick, thus the clients of this function are obligated to do
			  what they are asked for.

  `count` is actually the length of the payload data. Thus :
  count + MYSQLND_HEADER_SIZE = sizeof(buffer) (not the pointer but the actual buffer)
*/
static size_t
MYSQLND_METHOD(mysqlnd_net, send_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count,
									 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
{
	zend_uchar safe_buf[((MYSQLND_HEADER_SIZE) + (sizeof(zend_uchar)) - 1) / (sizeof(zend_uchar))];
	zend_uchar * safe_storage = safe_buf;
	size_t bytes_sent, packets_sent = 1;
	size_t left = count;
	zend_uchar * p = (zend_uchar *) buffer;
	zend_uchar * compress_buf = NULL;
	size_t to_be_sent;

	DBG_ENTER("mysqlnd_net::send_ex");
	DBG_INF_FMT("count=" MYSQLND_SZ_T_SPEC " compression=%u", count, net->data->compressed);

	if (net->data->compressed == TRUE) {
		size_t comp_buf_size = MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE + MIN(left, MYSQLND_MAX_PACKET_SIZE);
		DBG_INF_FMT("compress_buf_size="MYSQLND_SZ_T_SPEC, comp_buf_size);
		compress_buf = mnd_emalloc(comp_buf_size);
	}

	do {
		to_be_sent = MIN(left, MYSQLND_MAX_PACKET_SIZE);
		DBG_INF_FMT("to_be_sent=%u", to_be_sent);
		DBG_INF_FMT("packets_sent=%u", packets_sent);
		DBG_INF_FMT("compressed_envelope_packet_no=%u", net->compressed_envelope_packet_no);
		DBG_INF_FMT("packet_no=%u", net->packet_no);
#ifdef MYSQLND_COMPRESSION_ENABLED
		if (net->data->compressed == TRUE) {
			/* here we need to compress the data and then write it, first comes the compressed header */
			size_t tmp_complen = to_be_sent;
			size_t payload_size;
			zend_uchar * uncompressed_payload = p; /* should include the header */

			STORE_HEADER_SIZE(safe_storage, uncompressed_payload);
			int3store(uncompressed_payload, to_be_sent);
			int1store(uncompressed_payload + 3, net->packet_no);
			if (PASS == net->data->m.encode((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen,
									   uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE))
			{
				int3store(compress_buf + MYSQLND_HEADER_SIZE, to_be_sent + MYSQLND_HEADER_SIZE);
				payload_size = tmp_complen;
			} else {
				int3store(compress_buf + MYSQLND_HEADER_SIZE, 0);
				memcpy(compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE);
				payload_size = to_be_sent + MYSQLND_HEADER_SIZE;
			}
			RESTORE_HEADER_SIZE(uncompressed_payload, safe_storage);

			int3store(compress_buf, payload_size);
			int1store(compress_buf + 3, net->packet_no);
			DBG_INF_FMT("writing "MYSQLND_SZ_T_SPEC" bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE);
			bytes_sent = net->data->m.network_write_ex(net, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE,
												 conn_stats, error_info);
			net->compressed_envelope_packet_no++;
  #if WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY
			if (res == Z_OK) {
				size_t decompressed_size = left + MYSQLND_HEADER_SIZE;
				zend_uchar * decompressed_data = mnd_malloc(decompressed_size);
				int error = net->data->m.decode(decompressed_data, decompressed_size,
										  compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, payload_size);
				if (error == Z_OK) {
					int i;
					DBG_INF("success decompressing");
					for (i = 0 ; i < decompressed_size; i++) {
						if (i && (i % 30 == 0)) {
							printf("\n\t\t");
						}
						printf("%.2X ", (int)*((char*)&(decompressed_data[i])));
						DBG_INF_FMT("%.2X ", (int)*((char*)&(decompressed_data[i])));
					}
				} else {
					DBG_INF("error decompressing");
				}
				mnd_free(decompressed_data);
			}
  #endif /* WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY */
		} else
#endif /* MYSQLND_COMPRESSION_ENABLED */
		{
			DBG_INF("no compression");
			STORE_HEADER_SIZE(safe_storage, p);
			int3store(p, to_be_sent);
			int1store(p + 3, net->packet_no);
			bytes_sent = net->data->m.network_write_ex(net, p, to_be_sent + MYSQLND_HEADER_SIZE, conn_stats, error_info);
			RESTORE_HEADER_SIZE(p, safe_storage);
			net->compressed_envelope_packet_no++;
		}
		net->packet_no++;

		p += to_be_sent;
		left -= to_be_sent;
		packets_sent++;
		/*
		  if left is 0 then there is nothing more to send, but if the last packet was exactly
		  with the size MYSQLND_MAX_PACKET_SIZE we need to send additional packet, which has
		  empty payload. Thus if left == 0 we check for to_be_sent being the max size. If it is
		  indeed it then loop once more, then to_be_sent will become 0, left will stay 0. Empty
		  packet will be sent and this loop will end.
		*/
	} while (bytes_sent && (left > 0 || to_be_sent == MYSQLND_MAX_PACKET_SIZE));

	DBG_INF_FMT("packet_size="MYSQLND_SZ_T_SPEC" packet_no=%u", left, net->packet_no);

	MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats,
			STAT_BYTES_SENT, count + packets_sent * MYSQLND_HEADER_SIZE,
			STAT_PROTOCOL_OVERHEAD_OUT, packets_sent * MYSQLND_HEADER_SIZE,
			STAT_PACKETS_SENT, packets_sent);

	if (compress_buf) {
		mnd_efree(compress_buf);
	}

	/* Even for zero size payload we have to send a packet */
	if (!bytes_sent) {
		DBG_ERR_FMT("Can't %u send bytes", count);
		SET_CLIENT_ERROR(*error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
	}
	DBG_RETURN(bytes_sent);
}
Esempio n. 15
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_SESSION_OPTIONS * const session_options,
							  const zend_ulong mysql_flags,
							  const unsigned int server_charset_no,
							  const zend_bool use_full_blown_auth_packet,
							  const char * const auth_protocol,
							  struct st_mysqlnd_authentication_plugin * auth_plugin,
							  const zend_uchar * const orig_auth_plugin_data,
							  const size_t orig_auth_plugin_data_len,
							  const zend_uchar * const auth_plugin_data,
							  const size_t auth_plugin_data_len,
							  char ** switch_to_auth_protocol,
							  size_t * const switch_to_auth_protocol_len,
							  zend_uchar ** switch_to_auth_protocol_data,
							  size_t * const switch_to_auth_protocol_data_len
							 )
{
	enum_func_status ret = FAIL;
	const MYSQLND_CHARSET * charset = NULL;
	MYSQLND_PACKET_AUTH_RESPONSE auth_resp_packet;

	DBG_ENTER("mysqlnd_auth_handshake");

	conn->payload_decoder_factory->m.init_auth_response_packet(&auth_resp_packet);

	if (use_full_blown_auth_packet != TRUE) {
		MYSQLND_PACKET_CHANGE_AUTH_RESPONSE change_auth_resp_packet;

		conn->payload_decoder_factory->m.init_change_auth_response_packet(&change_auth_resp_packet);

		change_auth_resp_packet.auth_data = auth_plugin_data;
		change_auth_resp_packet.auth_data_len = auth_plugin_data_len;

		if (!PACKET_WRITE(conn, &change_auth_resp_packet)) {
			SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
			SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
			PACKET_FREE(&change_auth_resp_packet);
			goto end;
		}
		PACKET_FREE(&change_auth_resp_packet);
	} else {
		MYSQLND_PACKET_AUTH auth_packet;

		conn->payload_decoder_factory->m.init_auth_packet(&auth_packet);

		auth_packet.client_flags = mysql_flags;
		auth_packet.max_packet_size = session_options->max_allowed_packet;
		if (session_options->charset_name && (charset = mysqlnd_find_charset_name(session_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(conn, &auth_packet)) {
			PACKET_FREE(&auth_packet);
			goto end;
		}

		if (use_full_blown_auth_packet == TRUE) {
			conn->charset = mysqlnd_find_charset_nr(auth_packet.charset_no);
		}

		PACKET_FREE(&auth_packet);
	}

	if (auth_plugin && auth_plugin->methods.handle_server_response) {
		auth_plugin->methods.handle_server_response(auth_plugin, conn,
				orig_auth_plugin_data, orig_auth_plugin_data_len, passwd, passwd_len);
	}

	if (FAIL == PACKET_READ(conn, &auth_resp_packet) || 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.s, conn->last_message.l, auth_resp_packet.message, auth_resp_packet.message_len);
	ret = PASS;
end:
	PACKET_FREE(&auth_resp_packet);
	DBG_RETURN(ret);
}