mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
Client attributes
This commit is contained in:
@ -37,6 +37,7 @@
|
||||
#include <my_global.h>
|
||||
#include <my_default.h>
|
||||
#include "mysql.h"
|
||||
#include "hash.h"
|
||||
|
||||
/* Remove client convenience wrappers */
|
||||
#undef max_allowed_packet
|
||||
@ -1216,17 +1217,30 @@ static int add_init_command(struct st_mysql_options *options, const char *cmd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define EXTENSION_SET(OPTS, X, VAL) \
|
||||
if (!(OPTS)->extension) \
|
||||
|
||||
#define ALLOCATE_EXTENSIONS(OPTS) \
|
||||
(OPTS)->extension= (struct st_mysql_options_extention *) \
|
||||
my_malloc(sizeof(struct st_mysql_options_extention), \
|
||||
MYF(MY_WME | MY_ZEROFILL)); \
|
||||
(OPTS)->extension->X= (VAL);
|
||||
MYF(MY_WME | MY_ZEROFILL)) \
|
||||
|
||||
|
||||
#define ENSURE_EXTENSIONS_PRESENT(OPTS) \
|
||||
do { \
|
||||
if (!(OPTS)->extension) \
|
||||
ALLOCATE_EXTENSIONS(OPTS); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define EXTENSION_SET_STRING(OPTS, X, STR) \
|
||||
if ((OPTS)->extension) \
|
||||
my_free((OPTS)->extension->X); \
|
||||
EXTENSION_SET(OPTS, X, (STR) ? my_strdup((STR), MYF(MY_WME)) : NULL);
|
||||
do { \
|
||||
if ((OPTS)->extension) \
|
||||
my_free((OPTS)->extension->X); \
|
||||
else \
|
||||
ALLOCATE_EXTENSIONS(OPTS); \
|
||||
(OPTS)->extension->X= ((STR) != NULL) ? \
|
||||
my_strdup((STR), MYF(MY_WME)) : NULL; \
|
||||
} while (0)
|
||||
|
||||
|
||||
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
|
||||
#define SET_SSL_OPTION(OPTS, opt_var, arg) \
|
||||
@ -2376,6 +2390,7 @@ static auth_plugin_t old_password_client_plugin=
|
||||
old_password_auth_client
|
||||
};
|
||||
|
||||
|
||||
struct st_mysql_client_plugin *mysql_client_builtins[]=
|
||||
{
|
||||
(struct st_mysql_client_plugin *)&native_password_client_plugin,
|
||||
@ -2383,6 +2398,67 @@ struct st_mysql_client_plugin *mysql_client_builtins[]=
|
||||
0
|
||||
};
|
||||
|
||||
|
||||
static uchar *
|
||||
write_length_encoded_string3(uchar *buf, char *string, size_t length)
|
||||
{
|
||||
buf= net_store_length(buf, length);
|
||||
memcpy(buf, string, length);
|
||||
buf+= length;
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
uchar *
|
||||
send_client_connect_attrs(MYSQL *mysql, uchar *buf)
|
||||
{
|
||||
/* check if the server supports connection attributes */
|
||||
if (mysql->server_capabilities & CLIENT_CONNECT_ATTRS)
|
||||
{
|
||||
|
||||
/* Always store the length if the client supports it */
|
||||
buf= net_store_length(buf,
|
||||
mysql->options.extension ?
|
||||
mysql->options.extension->connection_attributes_length :
|
||||
0);
|
||||
|
||||
/* check if we have connection attributes */
|
||||
if (mysql->options.extension &&
|
||||
my_hash_inited(&mysql->options.extension->connection_attributes))
|
||||
{
|
||||
HASH *attrs= &mysql->options.extension->connection_attributes;
|
||||
ulong idx;
|
||||
|
||||
/* loop over and dump the connection attributes */
|
||||
for (idx= 0; idx < attrs->records; idx++)
|
||||
{
|
||||
LEX_STRING *attr= (LEX_STRING *) my_hash_element(attrs, idx);
|
||||
LEX_STRING *key= attr, *value= attr + 1;
|
||||
|
||||
/* we can't have zero length keys */
|
||||
DBUG_ASSERT(key->length);
|
||||
|
||||
buf= write_length_encoded_string3(buf, key->str, key->length);
|
||||
buf= write_length_encoded_string3(buf, value->str, value->length);
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
static size_t get_length_store_length(size_t length)
|
||||
{
|
||||
/* as defined in net_store_length */
|
||||
#define MAX_VARIABLE_STRING_LENGTH 9
|
||||
uchar length_buffer[MAX_VARIABLE_STRING_LENGTH], *ptr;
|
||||
|
||||
ptr= net_store_length(length_buffer, length);
|
||||
|
||||
return ptr - &length_buffer[0];
|
||||
}
|
||||
|
||||
|
||||
/* this is a "superset" of MYSQL_PLUGIN_VIO, in C++ I use inheritance */
|
||||
typedef struct {
|
||||
int (*read_packet)(struct st_plugin_vio *vio, uchar **buf);
|
||||
@ -2426,8 +2502,13 @@ static int send_change_user_packet(MCPVIO_EXT *mpvio,
|
||||
MYSQL *mysql= mpvio->mysql;
|
||||
char *buff, *end;
|
||||
int res= 1;
|
||||
size_t connect_attrs_len=
|
||||
(mysql->server_capabilities & CLIENT_CONNECT_ATTRS &&
|
||||
mysql->options.extension) ?
|
||||
mysql->options.extension->connection_attributes_length : 0;
|
||||
|
||||
buff= my_alloca(USERNAME_LENGTH+1 + data_len+1 + NAME_LEN+1 + 2 + NAME_LEN+1);
|
||||
buff= my_alloca(USERNAME_LENGTH + data_len + 1 + NAME_LEN + 2 + NAME_LEN +
|
||||
connect_attrs_len + 9 /* for the length of the attrs */);
|
||||
|
||||
end= strmake(buff, mysql->user, USERNAME_LENGTH) + 1;
|
||||
|
||||
@ -2464,6 +2545,8 @@ static int send_change_user_packet(MCPVIO_EXT *mpvio,
|
||||
if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
|
||||
end= strmake(end, mpvio->plugin->name, NAME_LEN) + 1;
|
||||
|
||||
end= (char *) send_client_connect_attrs(mysql, (uchar *) end);
|
||||
|
||||
res= simple_command(mysql, COM_CHANGE_USER,
|
||||
(uchar*)buff, (ulong)(end-buff), 1);
|
||||
|
||||
@ -2472,6 +2555,7 @@ error:
|
||||
return res;
|
||||
}
|
||||
|
||||
#define MAX_CONNECTION_ATTR_STORAGE_LENGTH 65536
|
||||
|
||||
/**
|
||||
sends a client authentication packet (second packet in the 3-way handshake)
|
||||
@ -2509,10 +2593,21 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
|
||||
MYSQL *mysql= mpvio->mysql;
|
||||
NET *net= &mysql->net;
|
||||
char *buff, *end;
|
||||
size_t buff_size;
|
||||
size_t connect_attrs_len=
|
||||
(mysql->server_capabilities & CLIENT_CONNECT_ATTRS &&
|
||||
mysql->options.extension) ?
|
||||
mysql->options.extension->connection_attributes_length : 0;
|
||||
|
||||
DBUG_ASSERT(connect_attrs_len < MAX_CONNECTION_ATTR_STORAGE_LENGTH);
|
||||
|
||||
/*
|
||||
see end= buff+32 below, fixed size of the packet is 32 bytes.
|
||||
+9 because data is a length encoded binary where meta data size is max 9.
|
||||
*/
|
||||
buff_size= 33 + USERNAME_LENGTH + data_len + 9 + NAME_LEN + NAME_LEN + connect_attrs_len + 9;
|
||||
buff= my_alloca(buff_size);
|
||||
|
||||
/* see end= buff+32 below, fixed size of the packet is 32 bytes */
|
||||
buff= my_alloca(33 + USERNAME_LENGTH + data_len + NAME_LEN + NAME_LEN);
|
||||
|
||||
mysql->client_flag|= mysql->options.client_flag;
|
||||
mysql->client_flag|= CLIENT_CAPABILITIES;
|
||||
|
||||
@ -2665,6 +2760,8 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
|
||||
if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
|
||||
end= strmake(end, mpvio->plugin->name, NAME_LEN) + 1;
|
||||
|
||||
end= (char *) send_client_connect_attrs(mysql, (uchar *) end);
|
||||
|
||||
/* Write authentication package */
|
||||
if (my_net_write(net, (uchar*) buff, (size_t) (end-buff)) || net_flush(net))
|
||||
{
|
||||
@ -3024,6 +3121,51 @@ connect_sync_or_async(MYSQL *mysql, NET *net, my_socket fd,
|
||||
return my_connect(fd, name, namelen, mysql->options.connect_timeout);
|
||||
}
|
||||
|
||||
|
||||
/** set some default attributes */
|
||||
static int
|
||||
set_connect_attributes(MYSQL *mysql, char *buff, size_t buf_len)
|
||||
{
|
||||
int rc= 0;
|
||||
|
||||
/*
|
||||
Clean up any values set by the client code. We want these options as
|
||||
consistent as possible
|
||||
*/
|
||||
rc+= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_client_name");
|
||||
rc+= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_os");
|
||||
rc+= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_platform");
|
||||
rc+= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_pid");
|
||||
rc+= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_thread");
|
||||
rc+= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_client_version");
|
||||
|
||||
/*
|
||||
Now let's set up some values
|
||||
*/
|
||||
rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
|
||||
"_client_name", "libmysql");
|
||||
rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
|
||||
"_client_version", PACKAGE_VERSION);
|
||||
rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
|
||||
"_os", SYSTEM_TYPE);
|
||||
rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
|
||||
"_platform", MACHINE_TYPE);
|
||||
#ifdef __WIN__
|
||||
snprintf(buff, buf_len, "%lu", (ulong) GetCurrentProcessId());
|
||||
#else
|
||||
snprintf(buff, buf_len, "%lu", (ulong) getpid());
|
||||
#endif
|
||||
rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_pid", buff);
|
||||
|
||||
#ifdef __WIN__
|
||||
snprintf(buff, buf_len, "%lu", (ulong) GetCurrentThreadId());
|
||||
rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_thread", buff);
|
||||
#endif
|
||||
|
||||
return rc > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
MYSQL * STDCALL
|
||||
CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
|
||||
const char *passwd, const char *db,
|
||||
@ -3057,6 +3199,9 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
if (set_connect_attributes(mysql, buff, sizeof(buff)))
|
||||
DBUG_RETURN(0);
|
||||
|
||||
mysql->methods= &client_methods;
|
||||
net->vio = 0; /* If something goes wrong */
|
||||
mysql->client_flag=0; /* For handshake */
|
||||
@ -3757,6 +3902,7 @@ static void mysql_close_free_options(MYSQL *mysql)
|
||||
struct mysql_async_context *ctxt= mysql->options.extension->async_context;
|
||||
my_free(mysql->options.extension->plugin_dir);
|
||||
my_free(mysql->options.extension->default_auth);
|
||||
my_hash_free(&mysql->options.extension->connection_attributes);
|
||||
if (ctxt)
|
||||
{
|
||||
my_context_destroy(&ctxt->async_context);
|
||||
@ -4320,7 +4466,8 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg)
|
||||
my_free(ctxt);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
EXTENSION_SET(&(mysql->options), async_context, ctxt)
|
||||
ENSURE_EXTENSIONS_PRESENT(&(mysql->options));
|
||||
mysql->options.extension->async_context= ctxt;
|
||||
if (mysql->net.vio)
|
||||
mysql->net.vio->async_context= ctxt;
|
||||
break;
|
||||
@ -4345,6 +4492,145 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg)
|
||||
case MYSQL_OPT_SSL_CRLPATH:
|
||||
EXTENSION_SET_SSL_STRING(&mysql->options, ssl_crlpath, arg);
|
||||
break;
|
||||
case MYSQL_OPT_CONNECT_ATTR_RESET:
|
||||
ENSURE_EXTENSIONS_PRESENT(&mysql->options);
|
||||
if (my_hash_inited(&mysql->options.extension->connection_attributes))
|
||||
{
|
||||
my_hash_free(&mysql->options.extension->connection_attributes);
|
||||
mysql->options.extension->connection_attributes_length= 0;
|
||||
}
|
||||
break;
|
||||
case MYSQL_OPT_CONNECT_ATTR_DELETE:
|
||||
ENSURE_EXTENSIONS_PRESENT(&mysql->options);
|
||||
if (my_hash_inited(&mysql->options.extension->connection_attributes))
|
||||
{
|
||||
size_t len;
|
||||
uchar *elt;
|
||||
|
||||
len= arg ? strlen(arg) : 0;
|
||||
|
||||
if (len)
|
||||
{
|
||||
elt= my_hash_search(&mysql->options.extension->connection_attributes,
|
||||
arg, len);
|
||||
if (elt)
|
||||
{
|
||||
LEX_STRING *attr= (LEX_STRING *) elt;
|
||||
LEX_STRING *key= attr, *value= attr + 1;
|
||||
|
||||
mysql->options.extension->connection_attributes_length-=
|
||||
get_length_store_length(key->length) + key->length +
|
||||
get_length_store_length(value->length) + value->length;
|
||||
|
||||
my_hash_delete(&mysql->options.extension->connection_attributes,
|
||||
elt);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
/**
|
||||
A function to return the key from a connection attribute
|
||||
*/
|
||||
uchar *
|
||||
get_attr_key(LEX_STRING *part, size_t *length,
|
||||
my_bool not_used __attribute__((unused)))
|
||||
{
|
||||
*length= part[0].length;
|
||||
return (uchar *) part[0].str;
|
||||
}
|
||||
|
||||
int STDCALL
|
||||
mysql_options4(MYSQL *mysql,enum mysql_option option,
|
||||
const void *arg1, const void *arg2)
|
||||
{
|
||||
DBUG_ENTER("mysql_option");
|
||||
DBUG_PRINT("enter",("option: %d",(int) option));
|
||||
|
||||
switch (option)
|
||||
{
|
||||
case MYSQL_OPT_CONNECT_ATTR_ADD:
|
||||
{
|
||||
LEX_STRING *elt;
|
||||
char *key, *value;
|
||||
size_t key_len= arg1 ? strlen(arg1) : 0,
|
||||
value_len= arg2 ? strlen(arg2) : 0;
|
||||
size_t attr_storage_length= key_len + value_len;
|
||||
|
||||
/* we can't have a zero length key */
|
||||
if (!key_len)
|
||||
{
|
||||
set_mysql_error(mysql, CR_INVALID_PARAMETER_NO, unknown_sqlstate);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
/* calculate the total storage length of the attribute */
|
||||
attr_storage_length+= get_length_store_length(key_len);
|
||||
attr_storage_length+= get_length_store_length(value_len);
|
||||
|
||||
ENSURE_EXTENSIONS_PRESENT(&mysql->options);
|
||||
|
||||
/*
|
||||
Throw and error if the maximum combined length of the attribute value
|
||||
will be greater than the maximum that we can safely transmit.
|
||||
*/
|
||||
if (attr_storage_length +
|
||||
mysql->options.extension->connection_attributes_length >
|
||||
MAX_CONNECTION_ATTR_STORAGE_LENGTH)
|
||||
{
|
||||
set_mysql_error(mysql, CR_INVALID_PARAMETER_NO, unknown_sqlstate);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
if (!my_hash_inited(&mysql->options.extension->connection_attributes))
|
||||
{
|
||||
if (my_hash_init(&mysql->options.extension->connection_attributes,
|
||||
&my_charset_bin, 0, 0, 0, (my_hash_get_key) get_attr_key,
|
||||
my_free, HASH_UNIQUE))
|
||||
{
|
||||
set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
}
|
||||
if (!my_multi_malloc(MY_WME,
|
||||
&elt, 2 * sizeof(LEX_STRING),
|
||||
&key, key_len + 1,
|
||||
&value, value_len + 1,
|
||||
NULL))
|
||||
{
|
||||
set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
elt[0].str= key; elt[0].length= key_len;
|
||||
elt[1].str= value; elt[1].length= value_len;
|
||||
if (key_len)
|
||||
memcpy(key, arg1, key_len);
|
||||
key[key_len]= 0;
|
||||
if (value_len)
|
||||
memcpy(value, arg2, value_len);
|
||||
value[value_len]= 0;
|
||||
if (my_hash_insert(&mysql->options.extension->connection_attributes,
|
||||
(uchar *) elt))
|
||||
{
|
||||
/* can't insert the value */
|
||||
my_free(elt);
|
||||
set_mysql_error(mysql, CR_DUPLICATE_CONNECTION_ATTR,
|
||||
unknown_sqlstate);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
mysql->options.extension->connection_attributes_length+=
|
||||
attr_storage_length;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
Reference in New Issue
Block a user