summaryrefslogtreecommitdiff
path: root/sql-common
diff options
context:
space:
mode:
Diffstat (limited to 'sql-common')
-rw-r--r--sql-common/Makefile.am5
-rw-r--r--sql-common/client.c1150
-rw-r--r--sql-common/client_plugin.c447
-rw-r--r--sql-common/my_time.c82
4 files changed, 1412 insertions, 272 deletions
diff --git a/sql-common/Makefile.am b/sql-common/Makefile.am
index 614ccffde9d..2f5a049085f 100644
--- a/sql-common/Makefile.am
+++ b/sql-common/Makefile.am
@@ -14,7 +14,4 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
## Process this file with automake to create Makefile.in
-EXTRA_DIST = client.c pack.c my_time.c my_user.c
-
-# Don't update the files from bitkeeper
-%::SCCS/s.%
+EXTRA_DIST = client.c pack.c my_time.c my_user.c client_plugin.c
diff --git a/sql-common/client.c b/sql-common/client.c
index 1b7723ce0a5..28b3cf274bc 100644
--- a/sql-common/client.c
+++ b/sql-common/client.c
@@ -107,6 +107,10 @@ my_bool net_flush(NET *net);
#include "client_settings.h"
#include <sql_common.h>
+#include <mysql/client_plugin.h>
+
+#define native_password_plugin_name "mysql_native_password"
+#define old_password_plugin_name "mysql_old_password"
uint mysql_port=0;
char *mysql_unix_port= 0;
@@ -332,7 +336,7 @@ void net_clear_error(NET *net)
@param ... variable number of arguments
*/
-static void set_mysql_extended_error(MYSQL *mysql, int errcode,
+void set_mysql_extended_error(MYSQL *mysql, int errcode,
const char *sqlstate,
const char *format, ...)
{
@@ -1002,9 +1006,20 @@ static const char *default_options[]=
"replication-probe", "enable-reads-from-master", "repl-parse-query",
"ssl-cipher", "max-allowed-packet", "protocol", "shared-memory-base-name",
"multi-results", "multi-statements", "multi-queries", "secure-auth",
- "report-data-truncation",
+ "report-data-truncation", "plugin-dir", "default-auth",
NullS
};
+enum option_id {
+ OPT_port=1, OPT_socket, OPT_compress, OPT_password, OPT_pipe, OPT_timeout, OPT_user,
+ OPT_init_command, OPT_host, OPT_database, OPT_debug, OPT_return_found_rows,
+ OPT_ssl_key, OPT_ssl_cert, OPT_ssl_ca, OPT_ssl_capath,
+ OPT_character_sets_dir, OPT_default_character_set, OPT_interactive_timeout,
+ OPT_connect_timeout, OPT_local_infile, OPT_disable_local_infile,
+ OPT_replication_probe, OPT_enable_reads_from_master, OPT_repl_parse_query,
+ OPT_ssl_cipher, OPT_max_allowed_packet, OPT_protocol, OPT_shared_memory_base_name,
+ OPT_multi_results, OPT_multi_statements, OPT_multi_queries, OPT_secure_auth,
+ OPT_report_data_truncation, OPT_plugin_dir, OPT_default_auth,
+};
static TYPELIB option_types={array_elements(default_options)-1,
"options",default_options, NULL};
@@ -1022,7 +1037,7 @@ static int add_init_command(struct st_mysql_options *options, const char *cmd)
{
options->init_commands= (DYNAMIC_ARRAY*)my_malloc(sizeof(DYNAMIC_ARRAY),
MYF(MY_WME));
- init_dynamic_array(options->init_commands,sizeof(char*),0,5 CALLER_INFO);
+ init_dynamic_array(options->init_commands,sizeof(char*),5,5 CALLER_INFO);
}
if (!(tmp= my_strdup(cmd,MYF(MY_WME))) ||
@@ -1035,17 +1050,30 @@ static int add_init_command(struct st_mysql_options *options, const char *cmd)
return 0;
}
+#define extension_set_string(OPTS, X, STR) \
+ if ((OPTS)->extension) \
+ my_free((OPTS)->extension->X, MYF(MY_ALLOW_ZERO_PTR)); \
+ else \
+ (OPTS)->extension= (struct st_mysql_options_extention *) \
+ my_malloc(sizeof(struct st_mysql_options_extention), \
+ MYF(MY_WME | MY_ZEROFILL)); \
+ (OPTS)->extension->X= my_strdup((STR), MYF(MY_WME));
+
void mysql_read_default_options(struct st_mysql_options *options,
const char *filename,const char *group)
{
int argc;
char *argv_buff[1],**argv;
- const char *groups[3];
+ const char *groups[5];
DBUG_ENTER("mysql_read_default_options");
DBUG_PRINT("enter",("file: %s group: %s",filename,group ? group :"NULL"));
argc=1; argv=argv_buff; argv_buff[0]= (char*) "client";
- groups[0]= (char*) "client"; groups[1]= (char*) group; groups[2]=0;
+ groups[0]= (char*) "client";
+ groups[1]= (char*) "client-server";
+ groups[2]= (char*) "client-mariadb";
+ groups[3]= (char*) group;
+ groups[4]=0;
my_load_defaults(filename, groups, &argc, &argv, NULL);
if (argc != 1) /* If some default option */
@@ -1067,134 +1095,134 @@ void mysql_read_default_options(struct st_mysql_options *options,
for (end= *option ; *(end= strcend(end,'_')) ; )
*end= '-';
switch (find_type(*option+2,&option_types,2)) {
- case 1: /* port */
+ case OPT_port:
if (opt_arg)
options->port=atoi(opt_arg);
break;
- case 2: /* socket */
+ case OPT_socket:
if (opt_arg)
{
my_free(options->unix_socket,MYF(MY_ALLOW_ZERO_PTR));
options->unix_socket=my_strdup(opt_arg,MYF(MY_WME));
}
break;
- case 3: /* compress */
+ case OPT_compress:
options->compress=1;
options->client_flag|= CLIENT_COMPRESS;
break;
- case 4: /* password */
+ case OPT_password:
if (opt_arg)
{
my_free(options->password,MYF(MY_ALLOW_ZERO_PTR));
options->password=my_strdup(opt_arg,MYF(MY_WME));
}
break;
- case 5:
+ case OPT_pipe:
options->protocol = MYSQL_PROTOCOL_PIPE;
- case 20: /* connect_timeout */
- case 6: /* timeout */
+ case OPT_connect_timeout:
+ case OPT_timeout:
if (opt_arg)
options->connect_timeout=atoi(opt_arg);
break;
- case 7: /* user */
+ case OPT_user:
if (opt_arg)
{
my_free(options->user,MYF(MY_ALLOW_ZERO_PTR));
options->user=my_strdup(opt_arg,MYF(MY_WME));
}
break;
- case 8: /* init-command */
+ case OPT_init_command:
add_init_command(options,opt_arg);
break;
- case 9: /* host */
+ case OPT_host:
if (opt_arg)
{
my_free(options->host,MYF(MY_ALLOW_ZERO_PTR));
options->host=my_strdup(opt_arg,MYF(MY_WME));
}
break;
- case 10: /* database */
+ case OPT_database:
if (opt_arg)
{
my_free(options->db,MYF(MY_ALLOW_ZERO_PTR));
options->db=my_strdup(opt_arg,MYF(MY_WME));
}
break;
- case 11: /* debug */
+ case OPT_debug:
#ifdef MYSQL_CLIENT
mysql_debug(opt_arg ? opt_arg : "d:t:o,/tmp/client.trace");
break;
#endif
- case 12: /* return-found-rows */
+ case OPT_return_found_rows:
options->client_flag|=CLIENT_FOUND_ROWS;
break;
#ifdef HAVE_OPENSSL
- case 13: /* ssl_key */
+ case OPT_ssl_key:
my_free(options->ssl_key, MYF(MY_ALLOW_ZERO_PTR));
options->ssl_key = my_strdup(opt_arg, MYF(MY_WME));
break;
- case 14: /* ssl_cert */
+ case OPT_ssl_cert:
my_free(options->ssl_cert, MYF(MY_ALLOW_ZERO_PTR));
options->ssl_cert = my_strdup(opt_arg, MYF(MY_WME));
break;
- case 15: /* ssl_ca */
+ case OPT_ssl_ca:
my_free(options->ssl_ca, MYF(MY_ALLOW_ZERO_PTR));
options->ssl_ca = my_strdup(opt_arg, MYF(MY_WME));
break;
- case 16: /* ssl_capath */
+ case OPT_ssl_capath:
my_free(options->ssl_capath, MYF(MY_ALLOW_ZERO_PTR));
options->ssl_capath = my_strdup(opt_arg, MYF(MY_WME));
break;
- case 26: /* ssl_cipher */
+ case OPT_ssl_cipher:
my_free(options->ssl_cipher, MYF(MY_ALLOW_ZERO_PTR));
options->ssl_cipher= my_strdup(opt_arg, MYF(MY_WME));
break;
#else
- case 13: /* Ignore SSL options */
- case 14:
- case 15:
- case 16:
- case 26:
+ case OPT_ssl_key:
+ case OPT_ssl_cert:
+ case OPT_ssl_ca:
+ case OPT_ssl_capath:
+ case OPT_ssl_cipher:
break;
#endif /* HAVE_OPENSSL */
- case 17: /* charset-lib */
+ case OPT_character_sets_dir:
my_free(options->charset_dir,MYF(MY_ALLOW_ZERO_PTR));
options->charset_dir = my_strdup(opt_arg, MYF(MY_WME));
break;
- case 18:
+ case OPT_default_character_set:
my_free(options->charset_name,MYF(MY_ALLOW_ZERO_PTR));
options->charset_name = my_strdup(opt_arg, MYF(MY_WME));
break;
- case 19: /* Interactive-timeout */
+ case OPT_interactive_timeout:
options->client_flag|= CLIENT_INTERACTIVE;
break;
- case 21:
+ case OPT_local_infile:
if (!opt_arg || atoi(opt_arg) != 0)
options->client_flag|= CLIENT_LOCAL_FILES;
else
options->client_flag&= ~CLIENT_LOCAL_FILES;
break;
- case 22:
+ case OPT_disable_local_infile:
options->client_flag&= ~CLIENT_LOCAL_FILES;
break;
- case 23: /* replication probe */
+ case OPT_replication_probe:
#ifndef TO_BE_DELETED
options->rpl_probe= 1;
#endif
break;
- case 24: /* enable-reads-from-master */
+ case OPT_enable_reads_from_master:
options->no_master_reads= 0;
break;
- case 25: /* repl-parse-query */
+ case OPT_repl_parse_query:
#ifndef TO_BE_DELETED
options->rpl_parse= 1;
#endif
break;
- case 27:
+ case OPT_max_allowed_packet:
if (opt_arg)
options->max_allowed_packet= atoi(opt_arg);
break;
- case 28: /* protocol */
+ case OPT_protocol:
if ((options->protocol= find_type(opt_arg,
&sql_protocol_typelib,0)) <= 0)
{
@@ -1202,26 +1230,32 @@ void mysql_read_default_options(struct st_mysql_options *options,
exit(1);
}
break;
- case 29: /* shared_memory_base_name */
+ case OPT_shared_memory_base_name:
#ifdef HAVE_SMEM
if (options->shared_memory_base_name != def_shared_memory_base_name)
my_free(options->shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR));
options->shared_memory_base_name=my_strdup(opt_arg,MYF(MY_WME));
#endif
break;
- case 30:
+ case OPT_multi_results:
options->client_flag|= CLIENT_MULTI_RESULTS;
break;
- case 31:
- case 32:
+ case OPT_multi_statements:
+ case OPT_multi_queries:
options->client_flag|= CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS;
break;
- case 33: /* secure-auth */
+ case OPT_secure_auth:
options->secure_auth= TRUE;
break;
- case 34: /* report-data-truncation */
+ case OPT_report_data_truncation:
options->report_data_truncation= opt_arg ? test(atoi(opt_arg)) : 1;
break;
+ case OPT_plugin_dir:
+ extension_set_string(options, plugin_dir, opt_arg);
+ break;
+ case OPT_default_auth:
+ extension_set_string(options, default_auth, opt_arg);
+ break;
default:
DBUG_PRINT("warning",("unknown option: %s",option[0]));
}
@@ -1590,6 +1624,7 @@ mysql_init(MYSQL *mysql)
*/
mysql->reconnect= 0;
+ DBUG_PRINT("mysql",("mysql: 0x%lx", (long) mysql));
return mysql;
}
@@ -1611,6 +1646,11 @@ mysql_ssl_set(MYSQL *mysql __attribute__((unused)) ,
{
DBUG_ENTER("mysql_ssl_set");
#ifdef HAVE_OPENSSL
+ my_free(mysql->options.ssl_key, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(mysql->options.ssl_cert, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(mysql->options.ssl_ca, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(mysql->options.ssl_capath, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(mysql->options.ssl_cipher, MYF(MY_ALLOW_ZERO_PTR));
mysql->options.ssl_key= strdup_if_not_null(key);
mysql->options.ssl_cert= strdup_if_not_null(cert);
mysql->options.ssl_ca= strdup_if_not_null(ca);
@@ -1760,6 +1800,11 @@ static int ssl_verify_server_cert(Vio *vio, const char* server_hostname)
static my_bool cli_read_query_result(MYSQL *mysql);
static MYSQL_RES *cli_use_result(MYSQL *mysql);
+int cli_read_change_user_result(MYSQL *mysql)
+{
+ return cli_safe_read(mysql);
+}
+
static MYSQL_METHODS client_methods=
{
cli_read_query_result, /* read_query_result */
@@ -1767,7 +1812,8 @@ static MYSQL_METHODS client_methods=
cli_read_rows, /* read_rows */
cli_use_result, /* use_result */
cli_fetch_lengths, /* fetch_lengths */
- cli_flush_use_result /* flush_use_result */
+ cli_flush_use_result, /* flush_use_result */
+ cli_read_change_user_result /* read_change_user_result */
#ifndef MYSQL_SERVER
,cli_list_fields, /* list_fields */
cli_read_prepare_result, /* read_prepare_result */
@@ -1777,7 +1823,6 @@ static MYSQL_METHODS client_methods=
NULL, /* free_embedded_thd */
cli_read_statistics, /* read_statistics */
cli_read_query_result, /* next_result */
- cli_read_change_user_result, /* read_change_user_result */
cli_read_binary_rows /* read_rows_from_cursor */
#endif
};
@@ -1851,6 +1896,646 @@ int mysql_init_character_set(MYSQL *mysql)
}
C_MODE_END
+/*********** client side authentication support **************************/
+
+typedef struct st_mysql_client_plugin_AUTHENTICATION auth_plugin_t;
+static int client_mpvio_write_packet(struct st_plugin_vio*, const uchar*, int);
+static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql);
+static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql);
+
+static auth_plugin_t native_password_client_plugin=
+{
+ MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
+ MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION,
+ native_password_plugin_name,
+ "R.J.Silk, Sergei Golubchik",
+ "Native MySQL authentication",
+ {1, 0, 0},
+ NULL,
+ NULL,
+ native_password_auth_client
+};
+
+static auth_plugin_t old_password_client_plugin=
+{
+ MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
+ MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION,
+ old_password_plugin_name,
+ "R.J.Silk, Sergei Golubchik",
+ "Old MySQL-3.23 authentication",
+ {1, 0, 0},
+ NULL,
+ NULL,
+ old_password_auth_client
+};
+
+struct st_mysql_client_plugin *mysql_client_builtins[]=
+{
+ (struct st_mysql_client_plugin *)&native_password_client_plugin,
+ (struct st_mysql_client_plugin *)&old_password_client_plugin,
+ 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);
+ int (*write_packet)(struct st_plugin_vio *vio, const uchar *pkt, int pkt_len);
+ void (*info)(struct st_plugin_vio *vio, struct st_plugin_vio_info *info);
+ /* -= end of MYSQL_PLUGIN_VIO =- */
+ MYSQL *mysql;
+ auth_plugin_t *plugin; /**< what plugin we're under */
+ const char *db;
+ struct {
+ uchar *pkt; /**< pointer into NET::buff */
+ uint pkt_len;
+ } cached_server_reply;
+ uint packets_read, packets_written; /**< counters for send/received packets */
+ my_bool mysql_change_user; /**< if it's mysql_change_user() */
+ int last_read_packet_len; /**< the length of the last *read* packet */
+} MCPVIO_EXT;
+
+/**
+ sends a COM_CHANGE_USER command with a caller provided payload
+
+ Packet format:
+
+ Bytes Content
+ ----- ----
+ n user name - \0-terminated string
+ n password
+ 3.23 scramble - \0-terminated string (9 bytes)
+ otherwise - length (1 byte) coded
+ n database name - \0-terminated string
+ 2 character set number (if the server >= 4.1.x)
+ n client auth plugin name - \0-terminated string,
+ (if the server supports plugin auth)
+
+ @retval 0 ok
+ @retval 1 error
+*/
+
+static int send_change_user_packet(MCPVIO_EXT *mpvio,
+ const uchar *data, int data_len)
+{
+ MYSQL *mysql= mpvio->mysql;
+ char *buff, *end;
+ int res= 1;
+
+ buff= my_alloca(USERNAME_LENGTH+1 + data_len+1 + NAME_LEN+1 + 2 + NAME_LEN+1);
+
+ end= strmake(buff, mysql->user, USERNAME_LENGTH) + 1;
+
+ if (!data_len)
+ *end++= 0;
+ else
+ {
+ if (mysql->client_flag & CLIENT_SECURE_CONNECTION)
+ {
+ DBUG_ASSERT(data_len <= 255);
+ if (data_len > 255)
+ {
+ set_mysql_error(mysql, CR_MALFORMED_PACKET, unknown_sqlstate);
+ goto error;
+ }
+ *end++= data_len;
+ }
+ else
+ {
+ DBUG_ASSERT(data_len == SCRAMBLE_LENGTH_323 + 1);
+ DBUG_ASSERT(data[SCRAMBLE_LENGTH_323] == 0);
+ }
+ memcpy(end, data, data_len);
+ end+= data_len;
+ }
+ end= strmake(end, mpvio->db ? mpvio->db : "", NAME_LEN) + 1;
+
+ if (mysql->server_capabilities & CLIENT_PROTOCOL_41)
+ {
+ int2store(end, (ushort) mysql->charset->number);
+ end+= 2;
+ }
+
+ if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
+ end= strmake(end, mpvio->plugin->name, NAME_LEN) + 1;
+
+ res= simple_command(mysql, COM_CHANGE_USER,
+ (uchar*)buff, (ulong)(end-buff), 1);
+
+error:
+ my_afree(buff);
+ return res;
+}
+
+
+/**
+ sends a client authentication packet (second packet in the 3-way handshake)
+
+ Packet format (when the server is 4.0 or earlier):
+
+ Bytes Content
+ ----- ----
+ 2 client capabilities
+ 3 max packet size
+ n user name, \0-terminated
+ 9 scramble_323, \0-terminated
+
+ Packet format (when the server is 4.1 or newer):
+
+ Bytes Content
+ ----- ----
+ 4 client capabilities
+ 4 max packet size
+ 1 charset number
+ 23 reserved (always 0)
+ n user name, \0-terminated
+ n plugin auth data (e.g. scramble), length (1 byte) coded
+ n database name, \0-terminated
+ (if CLIENT_CONNECT_WITH_DB is set in the capabilities)
+ n client auth plugin name - \0-terminated string,
+ (if CLIENT_PLUGIN_AUTH is set in the capabilities)
+
+ @retval 0 ok
+ @retval 1 error
+*/
+
+static int send_client_reply_packet(MCPVIO_EXT *mpvio,
+ const uchar *data, int data_len)
+{
+ MYSQL *mysql= mpvio->mysql;
+ NET *net= &mysql->net;
+ char *buff, *end;
+
+ /* 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;
+
+ if (mysql->client_flag & CLIENT_MULTI_STATEMENTS)
+ mysql->client_flag|= CLIENT_MULTI_RESULTS;
+
+#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
+ if (mysql->options.ssl_key || mysql->options.ssl_cert ||
+ mysql->options.ssl_ca || mysql->options.ssl_capath ||
+ mysql->options.ssl_cipher)
+ mysql->options.use_ssl= 1;
+ if (mysql->options.use_ssl)
+ mysql->client_flag|= CLIENT_SSL;
+#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY*/
+ if (mpvio->db)
+ mysql->client_flag|= CLIENT_CONNECT_WITH_DB;
+
+ /* Remove options that server doesn't support */
+ mysql->client_flag= mysql->client_flag &
+ (~(CLIENT_COMPRESS | CLIENT_SSL | CLIENT_PROTOCOL_41)
+ | mysql->server_capabilities);
+
+#ifndef HAVE_COMPRESS
+ mysql->client_flag&= ~CLIENT_COMPRESS;
+#endif
+
+ if (mysql->client_flag & CLIENT_PROTOCOL_41)
+ {
+ /* 4.1 server and 4.1 client has a 32 byte option flag */
+ int4store(buff,mysql->client_flag);
+ int4store(buff+4, net->max_packet_size);
+ buff[8]= (char) mysql->charset->number;
+ bzero(buff+9, 32-9);
+ end= buff+32;
+ }
+ else
+ {
+ int2store(buff, mysql->client_flag);
+ int3store(buff+2, net->max_packet_size);
+ end= buff+5;
+ }
+#ifdef HAVE_OPENSSL
+ if (mysql->client_flag & CLIENT_SSL)
+ {
+ /* Do the SSL layering. */
+ struct st_mysql_options *options= &mysql->options;
+ struct st_VioSSLFd *ssl_fd;
+ char error_string[1024];
+
+ /*
+ Send mysql->client_flag, max_packet_size - unencrypted otherwise
+ the server does not know we want to do SSL
+ */
+ if (my_net_write(net, (uchar*)buff, (size_t) (end-buff)) || net_flush(net))
+ {
+ set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
+ ER(CR_SERVER_LOST_EXTENDED),
+ "sending connection information to server",
+ errno);
+ goto error;
+ }
+
+ /* Create the VioSSLConnectorFd - init SSL and load certs */
+ if (!(ssl_fd= new_VioSSLConnectorFd(options->ssl_key,
+ options->ssl_cert,
+ options->ssl_ca,
+ options->ssl_capath,
+ options->ssl_cipher)))
+ {
+ set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate);
+ goto error;
+ }
+ mysql->connector_fd= (void*)ssl_fd;
+
+ /* Connect to the server */
+ DBUG_PRINT("info", ("IO layer change in progress..."));
+ if (sslconnect(ssl_fd, net->vio,
+ (long) (mysql->options.connect_timeout),
+ error_string))
+ {
+ set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR,
+ unknown_sqlstate, "SSL error: %s",
+ error_string[0] ? error_string :
+ ER(CR_SSL_CONNECTION_ERROR));
+ goto error;
+ }
+ DBUG_PRINT("info", ("IO layer change done!"));
+
+ /* Verify server cert */
+ if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) &&
+ ssl_verify_server_cert(net->vio, mysql->host))
+ {
+ set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate);
+ goto error;
+ }
+ }
+#endif /* HAVE_OPENSSL */
+
+ DBUG_PRINT("info",("Server version = '%s' capabilites: %lu status: %u client_flag: %lu",
+ mysql->server_version, mysql->server_capabilities,
+ mysql->server_status, mysql->client_flag));
+
+ compile_time_assert(MYSQL_USERNAME_LENGTH == USERNAME_LENGTH);
+
+ /* This needs to be changed as it's not useful with big packets */
+ if (mysql->user[0])
+ strmake(end, mysql->user, USERNAME_LENGTH);
+ else
+ read_user_name(end);
+
+ /* We have to handle different version of handshake here */
+ DBUG_PRINT("info",("user: %s",end));
+ end= strend(end) + 1;
+ if (data_len)
+ {
+ if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
+ {
+ *end++= data_len;
+ memcpy(end, data, data_len);
+ end+= data_len;
+ }
+ else
+ {
+ DBUG_ASSERT(data_len == SCRAMBLE_LENGTH_323 + 1); /* incl. \0 at the end */
+ memcpy(end, data, data_len);
+ end+= data_len;
+ }
+ }
+ else
+ *end++= 0;
+
+ /* Add database if needed */
+ if (mpvio->db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB))
+ {
+ end= strmake(end, mpvio->db, NAME_LEN) + 1;
+ mysql->db= my_strdup(mpvio->db, MYF(MY_WME));
+ }
+
+ if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
+ end= strmake(end, mpvio->plugin->name, NAME_LEN) + 1;
+
+ /* Write authentication package */
+ if (my_net_write(net, (uchar*) buff, (size_t) (end-buff)) || net_flush(net))
+ {
+ set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
+ ER(CR_SERVER_LOST_EXTENDED),
+ "sending authentication information",
+ errno);
+ goto error;
+ }
+ my_afree(buff);
+ return 0;
+
+error:
+ my_afree(buff);
+ return 1;
+}
+
+
+/**
+ vio->read_packet() callback method for client authentication plugins
+
+ This function is called by a client authentication plugin, when it wants
+ to read data from the server.
+*/
+
+static int client_mpvio_read_packet(struct st_plugin_vio *mpv, uchar **buf)
+{
+ MCPVIO_EXT *mpvio= (MCPVIO_EXT*)mpv;
+ MYSQL *mysql= mpvio->mysql;
+ ulong pkt_len;
+
+ /* there are cached data left, feed it to a plugin */
+ if (mpvio->cached_server_reply.pkt)
+ {
+ *buf= mpvio->cached_server_reply.pkt;
+ mpvio->cached_server_reply.pkt= 0;
+ mpvio->packets_read++;
+ return mpvio->cached_server_reply.pkt_len;
+ }
+
+ if (mpvio->packets_read == 0)
+ {
+ /*
+ the server handshake packet came from the wrong plugin,
+ or it's mysql_change_user(). Either way, there is no data
+ for a plugin to read. send a dummy packet to the server
+ to initiate a dialog.
+ */
+ if (client_mpvio_write_packet(mpv, 0, 0))
+ return (int)packet_error;
+ }
+
+ /* otherwise read the data */
+ pkt_len= (*mysql->methods->read_change_user_result)(mysql);
+ mpvio->last_read_packet_len= pkt_len;
+ *buf= mysql->net.read_pos;
+
+ /* was it a request to change plugins ? */
+ if (**buf == 254)
+ return (int)packet_error; /* if yes, this plugin shan't continue */
+
+ /*
+ the server sends \1\255 or \1\254 instead of just \255 or \254 -
+ for us to not confuse it with an error or "change plugin" packets.
+ We remove this escaping \1 here.
+
+ See also server_mpvio_write_packet() where the escaping is done.
+ */
+ if (pkt_len && **buf == 1)
+ {
+ (*buf)++;
+ pkt_len--;
+ }
+ mpvio->packets_read++;
+ return pkt_len;
+}
+
+
+/**
+ vio->write_packet() callback method for client authentication plugins
+
+ This function is called by a client authentication plugin, when it wants
+ to send data to the server.
+
+ It transparently wraps the data into a change user or authentication
+ handshake packet, if neccessary.
+*/
+
+static int client_mpvio_write_packet(struct st_plugin_vio *mpv,
+ const uchar *pkt, int pkt_len)
+{
+ int res;
+ MCPVIO_EXT *mpvio= (MCPVIO_EXT*)mpv;
+
+ if (mpvio->packets_written == 0)
+ {
+ if (mpvio->mysql_change_user)
+ res= send_change_user_packet(mpvio, pkt, pkt_len);
+ else
+ res= send_client_reply_packet(mpvio, pkt, pkt_len);
+ }
+ else
+ {
+ NET *net= &mpvio->mysql->net;
+ if (mpvio->mysql->thd)
+ res= 1; /* no chit-chat in embedded */
+ else
+ res= my_net_write(net, pkt, pkt_len) || net_flush(net);
+ if (res)
+ set_mysql_extended_error(mpvio->mysql, CR_SERVER_LOST, unknown_sqlstate,
+ ER(CR_SERVER_LOST_EXTENDED),
+ "sending authentication information",
+ errno);
+ }
+ mpvio->packets_written++;
+ return res;
+}
+
+
+/**
+ fills MYSQL_PLUGIN_VIO_INFO structure with the information about the
+ connection
+*/
+
+void mpvio_info(Vio *vio, MYSQL_PLUGIN_VIO_INFO *info)
+{
+ bzero(info, sizeof(*info));
+ switch (vio->type) {
+ case VIO_TYPE_TCPIP:
+ info->protocol= MYSQL_VIO_TCP;
+ info->socket= vio->sd;
+ return;
+ case VIO_TYPE_SOCKET:
+ info->protocol= MYSQL_VIO_SOCKET;
+ info->socket= vio->sd;
+ return;
+ case VIO_TYPE_SSL:
+ {
+ struct sockaddr addr;
+ SOCKET_SIZE_TYPE addrlen= sizeof(addr);
+ if (getsockname(vio->sd, &addr, &addrlen))
+ return;
+ info->protocol= addr.sa_family == AF_UNIX ?
+ MYSQL_VIO_SOCKET : MYSQL_VIO_TCP;
+ info->socket= vio->sd;
+ return;
+ }
+#ifdef _WIN32
+ case VIO_TYPE_NAMEDPIPE:
+ info->protocol= MYSQL_VIO_PIPE;
+ info->handle= vio->hPipe;
+ return;
+ case VIO_TYPE_SHARED_MEMORY:
+ info->protocol= MYSQL_VIO_MEMORY;
+ info->handle= vio->handle_file_map; /* or what ? */
+ return;
+#endif
+ default: DBUG_ASSERT(0);
+ }
+}
+
+
+static void client_mpvio_info(MYSQL_PLUGIN_VIO *vio,
+ MYSQL_PLUGIN_VIO_INFO *info)
+{
+ MCPVIO_EXT *mpvio= (MCPVIO_EXT*)vio;
+ mpvio_info(mpvio->mysql->net.vio, info);
+}
+
+
+/**
+ Client side of the plugin driver authentication.
+
+ @note this is used by both the mysql_real_connect and mysql_change_user
+
+ @param mysql mysql
+ @param data pointer to the plugin auth data (scramble) in the
+ handshake packet
+ @param data_len the length of the data
+ @param data_plugin a plugin that data were prepared for
+ or 0 if it's mysql_change_user()
+ @param db initial db to use, can be 0
+
+ @retval 0 ok
+ @retval 1 error
+*/
+
+int run_plugin_auth(MYSQL *mysql, char *data, uint data_len,
+ const char *data_plugin, const char *db)
+{
+ const char *auth_plugin_name;
+ auth_plugin_t *auth_plugin;
+ MCPVIO_EXT mpvio;
+ ulong pkt_length;
+ int res;
+
+ /* determine the default/initial plugin to use */
+ if (mysql->options.extension && mysql->options.extension->default_auth &&
+ mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
+ {
+ auth_plugin_name= mysql->options.extension->default_auth;
+ if (!(auth_plugin= (auth_plugin_t*) mysql_client_find_plugin(mysql,
+ auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN)))
+ return 1; /* oops, not found */
+ }
+ else
+ {
+ auth_plugin= mysql->server_capabilities & CLIENT_PROTOCOL_41 ?
+ &native_password_client_plugin : &old_password_client_plugin;
+ auth_plugin_name= auth_plugin->name;
+ }
+
+ mysql->net.last_errno= 0; /* just in case */
+
+ if (data_plugin && strcmp(data_plugin, auth_plugin_name))
+ {
+ /* data was prepared for a different plugin, don't show it to this one */
+ data= 0;
+ data_len= 0;
+ }
+
+ mpvio.mysql_change_user= data_plugin == 0;
+ mpvio.cached_server_reply.pkt= (uchar*)data;
+ mpvio.cached_server_reply.pkt_len= data_len;
+ mpvio.read_packet= client_mpvio_read_packet;
+ mpvio.write_packet= client_mpvio_write_packet;
+ mpvio.info= client_mpvio_info;
+ mpvio.mysql= mysql;
+ mpvio.packets_read= mpvio.packets_written= 0;
+ mpvio.db= db;
+ mpvio.plugin= auth_plugin;
+
+ res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql);
+
+ compile_time_assert(CR_OK == -1);
+ compile_time_assert(CR_ERROR == 0);
+ if (res > CR_OK && mysql->net.read_pos[0] != 254)
+ {
+ /*
+ the plugin returned an error. write it down in mysql,
+ unless the error code is CR_ERROR and mysql->net.last_errno
+ is already set (the plugin has done it)
+ */
+ if (res > CR_ERROR)
+ set_mysql_error(mysql, res, unknown_sqlstate);
+ else
+ if (!mysql->net.last_errno)
+ set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate);
+ return 1;
+ }
+
+ /* read the OK packet (or use the cached value in mysql->net.read_pos */
+ if (res == CR_OK)
+ pkt_length= (*mysql->methods->read_change_user_result)(mysql);
+ else /* res == CR_OK_HANDSHAKE_COMPLETE */
+ pkt_length= mpvio.last_read_packet_len;
+
+ if (pkt_length == packet_error)
+ {
+ if (mysql->net.last_errno == CR_SERVER_LOST)
+ set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
+ ER(CR_SERVER_LOST_EXTENDED),
+ "reading authorization packet",
+ errno);
+ return 1;
+ }
+
+ if (mysql->net.read_pos[0] == 254)
+ {
+ /* The server asked to use a different authentication plugin */
+ if (pkt_length == 1)
+ {
+ /* old "use short scramble" packet */
+ auth_plugin_name= old_password_plugin_name;
+ mpvio.cached_server_reply.pkt= (uchar*)mysql->scramble;
+ mpvio.cached_server_reply.pkt_len= SCRAMBLE_LENGTH + 1;
+ }
+ else
+ {
+ /* new "use different plugin" packet */
+ uint len;
+ auth_plugin_name= (char*)mysql->net.read_pos + 1;
+ len= strlen(auth_plugin_name); /* safe as my_net_read always appends \0 */
+ mpvio.cached_server_reply.pkt_len= pkt_length - len - 2;
+ mpvio.cached_server_reply.pkt= mysql->net.read_pos + len + 2;
+ }
+
+ if (!(auth_plugin= (auth_plugin_t *) mysql_client_find_plugin(mysql,
+ auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN)))
+ return 1;
+
+ mpvio.plugin= auth_plugin;
+ res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql);
+
+ if (res > CR_OK)
+ {
+ if (res > CR_ERROR)
+ set_mysql_error(mysql, res, unknown_sqlstate);
+ else
+ if (!mysql->net.last_errno)
+ set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate);
+ return 1;
+ }
+
+ if (res != CR_OK_HANDSHAKE_COMPLETE)
+ {
+ /* Read what server thinks about out new auth message report */
+ if (cli_safe_read(mysql) == packet_error)
+ {
+ if (mysql->net.last_errno == CR_SERVER_LOST)
+ set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
+ ER(CR_SERVER_LOST_EXTENDED),
+ "reading final connect information",
+ errno);
+ return 1;
+ }
+ }
+ }
+ /*
+ net->read_pos[0] should always be 0 here if the server implements
+ the protocol correctly
+ */
+ return mysql->net.read_pos[0] != 0;
+}
+
MYSQL * STDCALL
CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
@@ -1858,7 +2543,10 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
uint port, const char *unix_socket,ulong client_flag)
{
char buff[NAME_LEN+USERNAME_LENGTH+100];
- char *end,*host_info= NULL;
+ int scramble_data_len, pkt_scramble_len;
+ char *end, *host_info=0, *server_version_end, *pkt_end;
+ char *scramble_data;
+ const char *scramble_plugin;
my_socket sock;
in_addr_t ip_addr;
struct sockaddr_in sock_addr;
@@ -1876,6 +2564,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
#endif
init_sigpipe_variables
DBUG_ENTER("mysql_real_connect");
+ LINT_INIT(pkt_scramble_len);
DBUG_PRINT("enter",("host: %s db: %s user: %s",
host ? host : "(Null)",
@@ -1934,7 +2623,8 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
#if defined(HAVE_SMEM)
if ((!mysql->options.protocol ||
mysql->options.protocol == MYSQL_PROTOCOL_MEMORY) &&
- (!host || !strcmp(host,LOCAL_HOST)))
+ (!host || !strcmp(host,LOCAL_HOST)) &&
+ mysql->options.shared_memory_base_name)
{
if ((create_shared_memory(mysql,net, mysql->options.connect_timeout)) ==
INVALID_HANDLE_VALUE)
@@ -1943,7 +2633,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
("host: '%s' socket: '%s' shared memory: %s have_tcpip: %d",
host ? host : "<null>",
unix_socket ? unix_socket : "<null>",
- (int) mysql->options.shared_memory_base_name,
+ mysql->options.shared_memory_base_name,
(int) have_tcpip));
if (mysql->options.protocol == MYSQL_PROTOCOL_MEMORY)
goto error;
@@ -2173,8 +2863,8 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
errno);
goto error;
}
+ pkt_end= (char*)net->read_pos + pkt_length;
/* Check if version of protocol matches current one */
-
mysql->protocol_version= net->read_pos[0];
DBUG_DUMP("packet",(uchar*) net->read_pos,10);
DBUG_PRINT("info",("mysql protocol version %d, server=%d",
@@ -2186,31 +2876,29 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
PROTOCOL_VERSION);
goto error;
}
- end=strend((char*) net->read_pos+1);
+ server_version_end= end= strend((char*) net->read_pos+1);
mysql->thread_id=uint4korr(end+1);
end+=5;
/*
- Scramble is split into two parts because old clients does not understand
+ Scramble is split into two parts because old clients do not understand
long scrambles; here goes the first part.
*/
- strmake(mysql->scramble, end, SCRAMBLE_LENGTH_323);
- end+= SCRAMBLE_LENGTH_323+1;
+ scramble_data= end;
+ scramble_data_len= SCRAMBLE_LENGTH_323 + 1;
+ scramble_plugin= old_password_plugin_name;
+ end+= scramble_data_len;
- if (pkt_length >= (uint) (end+1 - (char*) net->read_pos))
+ if (pkt_end >= end + 1)
mysql->server_capabilities=uint2korr(end);
- if (pkt_length >= (uint) (end+18 - (char*) net->read_pos))
+ if (pkt_end >= end + 18)
{
/* New protocol with 16 bytes to describe server characteristics */
mysql->server_language=end[2];
mysql->server_status=uint2korr(end+3);
+ mysql->server_capabilities|= uint2korr(end+5) << 16;
+ pkt_scramble_len= end[7];
}
end+= 18;
- if (pkt_length >= (uint) (end + SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323 + 1 -
- (char *) net->read_pos))
- strmake(mysql->scramble+SCRAMBLE_LENGTH_323, end,
- SCRAMBLE_LENGTH-SCRAMBLE_LENGTH_323);
- else
- mysql->server_capabilities&= ~CLIENT_SECURE_CONNECTION;
if (mysql->options.secure_auth && passwd[0] &&
!(mysql->server_capabilities & CLIENT_SECURE_CONNECTION))
@@ -2229,7 +2917,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
&mysql->unix_socket,unix_socket ?
(uint) strlen(unix_socket)+1 : (uint) 1,
&mysql->server_version,
- (uint) (end - (char*) net->read_pos),
+ (uint) (server_version_end - (char*) net->read_pos + 1),
NullS) ||
!(mysql->user=my_strdup(user,MYF(0))) ||
!(mysql->passwd=my_strdup(passwd,MYF(0))))
@@ -2246,198 +2934,47 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
strmov(mysql->server_version,(char*) net->read_pos+1);
mysql->port=port;
- /*
- Part 2: format and send client info to the server for access check
- */
-
- client_flag|=mysql->options.client_flag;
- client_flag|=CLIENT_CAPABILITIES;
- if (client_flag & CLIENT_MULTI_STATEMENTS)
- client_flag|= CLIENT_MULTI_RESULTS;
-
-#ifdef HAVE_OPENSSL
- if (mysql->options.ssl_key || mysql->options.ssl_cert ||
- mysql->options.ssl_ca || mysql->options.ssl_capath ||
- mysql->options.ssl_cipher)
- mysql->options.use_ssl= 1;
- if (mysql->options.use_ssl)
- client_flag|=CLIENT_SSL;
-#endif /* HAVE_OPENSSL */
- if (db)
- client_flag|=CLIENT_CONNECT_WITH_DB;
-
- /* Remove options that server doesn't support */
- client_flag= ((client_flag &
- ~(CLIENT_COMPRESS | CLIENT_SSL | CLIENT_PROTOCOL_41)) |
- (client_flag & mysql->server_capabilities));
-#ifndef HAVE_COMPRESS
- client_flag&= ~CLIENT_COMPRESS;
-#endif
-
- if (client_flag & CLIENT_PROTOCOL_41)
+ if (pkt_end >= end + SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323 + 1)
{
- /* 4.1 server and 4.1 client has a 32 byte option flag */
- int4store(buff,client_flag);
- int4store(buff+4, net->max_packet_size);
- buff[8]= (char) mysql->charset->number;
- bzero(buff+9, 32-9);
- end= buff+32;
- }
- else
- {
- int2store(buff,client_flag);
- int3store(buff+2,net->max_packet_size);
- end= buff+5;
- }
- mysql->client_flag=client_flag;
-
-#ifdef HAVE_OPENSSL
- if (client_flag & CLIENT_SSL)
- {
- /* Do the SSL layering. */
- struct st_mysql_options *options= &mysql->options;
- struct st_VioSSLFd *ssl_fd;
-
/*
- Send client_flag, max_packet_size - unencrypted otherwise
- the server does not know we want to do SSL
+ move the first scramble part - directly in the NET buffer -
+ to get a full continuous scramble. We've read all the header,
+ and can overwrite it now.
*/
- if (my_net_write(net, (uchar*) buff, (uint) (end-buff)) || net_flush(net))
+ memmove(end - SCRAMBLE_LENGTH_323, scramble_data,
+ SCRAMBLE_LENGTH_323);
+ scramble_data= end - SCRAMBLE_LENGTH_323;
+ if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
{
- set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
- ER(CR_SERVER_LOST_EXTENDED),
- "sending connection information to server",
- errno);
- goto error;
- }
-
- /* Create the VioSSLConnectorFd - init SSL and load certs */
- if (!(ssl_fd= new_VioSSLConnectorFd(options->ssl_key,
- options->ssl_cert,
- options->ssl_ca,
- options->ssl_capath,
- options->ssl_cipher)))
- {
- set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate);
- goto error;
- }
- mysql->connector_fd= (void*)ssl_fd;
-
- /* Connect to the server */
- DBUG_PRINT("info", ("IO layer change in progress..."));
- if (sslconnect(ssl_fd, mysql->net.vio,
- (long) (mysql->options.connect_timeout)))
- {
- set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate);
- goto error;
- }
- DBUG_PRINT("info", ("IO layer change done!"));
-
- /* Verify server cert */
- if ((client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) &&
- ssl_verify_server_cert(mysql->net.vio, mysql->host))
- {
- set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate);
- goto error;
- }
-
- }
-#endif /* HAVE_OPENSSL */
-
- DBUG_PRINT("info",("Server version = '%s' capabilites: %lu status: %u client_flag: %lu",
- mysql->server_version,mysql->server_capabilities,
- mysql->server_status, client_flag));
- /* This needs to be changed as it's not useful with big packets */
- if (user && user[0])
- strmake(end,user,USERNAME_LENGTH); /* Max user name */
- else
- read_user_name((char*) end);
-
- /* We have to handle different version of handshake here */
-#ifdef _CUSTOMCONFIG_
-#include "_cust_libmysql.h"
-#endif
- DBUG_PRINT("info",("user: %s",end));
- end= strend(end) + 1;
- if (passwd[0])
- {
- if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
- {
- *end++= SCRAMBLE_LENGTH;
- scramble(end, mysql->scramble, passwd);
- end+= SCRAMBLE_LENGTH;
+ scramble_data_len= pkt_scramble_len;
+ scramble_plugin= scramble_data + scramble_data_len;
+ if (scramble_data + scramble_data_len > pkt_end)
+ scramble_data_len= pkt_end - scramble_data;
}
else
{
- scramble_323(end, mysql->scramble, passwd);
- end+= SCRAMBLE_LENGTH_323 + 1;
+ scramble_data_len= pkt_end - scramble_data;
+ scramble_plugin= native_password_plugin_name;
}
}
else
- *end++= '\0'; /* empty password */
+ mysql->server_capabilities&= ~CLIENT_SECURE_CONNECTION;
+
+ mysql->client_flag= client_flag;
- /* Add database if needed */
- if (db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB))
- {
- end= strmake(end, db, NAME_LEN) + 1;
- mysql->db= my_strdup(db,MYF(MY_WME));
- db= 0;
- }
- /* Write authentication package */
- if (my_net_write(net, (uchar*) buff, (size_t) (end-buff)) || net_flush(net))
- {
- set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
- ER(CR_SERVER_LOST_EXTENDED),
- "sending authentication information",
- errno);
- goto error;
- }
-
/*
- Part 3: Authorization data's been sent. Now server can reply with
- OK-packet, or re-request scrambled password.
+ Part 2: invoke the plugin to send the authentication data to the server
*/
- if ((pkt_length=cli_safe_read(mysql)) == packet_error)
- {
- if (mysql->net.last_errno == CR_SERVER_LOST)
- set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
- ER(CR_SERVER_LOST_EXTENDED),
- "reading authorization packet",
- errno);
+ if (run_plugin_auth(mysql, scramble_data, scramble_data_len,
+ scramble_plugin, db))
goto error;
- }
- if (pkt_length == 1 && net->read_pos[0] == 254 &&
- mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
- {
- /*
- By sending this very specific reply server asks us to send scrambled
- password in old format.
- */
- scramble_323(buff, mysql->scramble, passwd);
- if (my_net_write(net, (uchar*) buff, SCRAMBLE_LENGTH_323 + 1) ||
- net_flush(net))
- {
- set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
- ER(CR_SERVER_LOST_EXTENDED),
- "sending password information",
- errno);
- goto error;
- }
- /* Read what server thinks about out new auth message report */
- if (cli_safe_read(mysql) == packet_error)
- {
- if (mysql->net.last_errno == CR_SERVER_LOST)
- set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
- ER(CR_SERVER_LOST_EXTENDED),
- "reading final connect information",
- errno);
- goto error;
- }
- }
+ /*
+ Part 3: authenticated, finish the initialization of the connection
+ */
- if (client_flag & CLIENT_COMPRESS) /* We will use compression */
+ if (mysql->client_flag & CLIENT_COMPRESS) /* We will use compression */
net->compress=1;
#ifdef CHECK_LICENSE
@@ -2445,7 +2982,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
goto error;
#endif
- if (db && mysql_select_db(mysql, db))
+ if (db && !mysql->db && mysql_select_db(mysql, db))
{
if (mysql->net.last_errno == CR_SERVER_LOST)
set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
@@ -2511,7 +3048,7 @@ error:
/* Free alloced memory */
end_server(mysql);
mysql_close_free(mysql);
- if (!(((ulong) client_flag) & CLIENT_REMEMBER_OPTIONS))
+ if (!(client_flag & CLIENT_REMEMBER_OPTIONS))
mysql_close_free_options(mysql);
}
DBUG_RETURN(0);
@@ -2656,6 +3193,12 @@ static void mysql_close_free_options(MYSQL *mysql)
if (mysql->options.shared_memory_base_name != def_shared_memory_base_name)
my_free(mysql->options.shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR));
#endif /* HAVE_SMEM */
+ if (mysql->options.extension)
+ {
+ my_free(mysql->options.extension->plugin_dir,MYF(MY_ALLOW_ZERO_PTR));
+ my_free(mysql->options.extension->default_auth,MYF(MY_ALLOW_ZERO_PTR));
+ my_free(mysql->options.extension,MYF(0));
+ }
bzero((char*) &mysql->options,sizeof(mysql->options));
DBUG_VOID_RETURN;
}
@@ -2754,9 +3297,18 @@ void mysql_detach_stmt_list(LIST **stmt_list __attribute__((unused)),
}
+/*
+ Close a MySQL connection and free all resources attached to it.
+
+ This function is coded in such that it can be called multiple times
+ (As some clients call this after mysql_real_connect() fails)
+*/
+
void STDCALL mysql_close(MYSQL *mysql)
{
DBUG_ENTER("mysql_close");
+ DBUG_PRINT("enter", ("mysql: 0x%lx", (long) mysql));
+
if (mysql) /* Some simple safety */
{
/* If connection is still up, send a QUIT message */
@@ -2787,10 +3339,16 @@ void STDCALL mysql_close(MYSQL *mysql)
}
#endif
if (mysql != mysql->master)
+ {
mysql_close(mysql->master);
+ mysql->master= 0;
+ }
#ifndef MYSQL_SERVER
if (mysql->thd)
+ {
(*mysql->methods->free_embedded_thd)(mysql);
+ mysql->thd= 0;
+ }
#endif
if (mysql->free_me)
my_free((uchar*) mysql,MYF(0));
@@ -3173,6 +3731,12 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg)
else
mysql->options.client_flag&= ~CLIENT_SSL_VERIFY_SERVER_CERT;
break;
+ case MYSQL_PLUGIN_DIR:
+ extension_set_string(&mysql->options, plugin_dir, arg);
+ break;
+ case MYSQL_DEFAULT_AUTH:
+ extension_set_string(&mysql->options, default_auth, arg);
+ break;
default:
DBUG_RETURN(1);
}
@@ -3216,7 +3780,7 @@ const char * STDCALL mysql_error(MYSQL *mysql)
mysql Connection
EXAMPLE
- 4.1.0-alfa -> 40100
+ MariaDB-4.1.0-alfa -> 40100
NOTES
We will ensure that a newer server always has a bigger number.
@@ -3229,7 +3793,11 @@ ulong STDCALL
mysql_get_server_version(MYSQL *mysql)
{
uint major, minor, version;
- char *pos= mysql->server_version, *end_pos;
+ const char *pos= mysql->server_version;
+ char *end_pos;
+ /* Skip possible prefix */
+ while (*pos && !my_isdigit(&my_charset_latin1, *pos))
+ pos++;
major= (uint) strtoul(pos, &end_pos, 10); pos=end_pos+1;
minor= (uint) strtoul(pos, &end_pos, 10); pos=end_pos+1;
version= (uint) strtoul(pos, &end_pos, 10);
@@ -3245,7 +3813,7 @@ mysql_get_server_version(MYSQL *mysql)
*/
int STDCALL mysql_set_character_set(MYSQL *mysql, const char *cs_name)
{
- struct charset_info_st *cs;
+ CHARSET_INFO *cs;
const char *save_csdir= charsets_dir;
if (mysql->options.charset_dir)
@@ -3277,3 +3845,99 @@ int STDCALL mysql_set_character_set(MYSQL *mysql, const char *cs_name)
}
+/**
+ client authentication plugin that does native MySQL authentication
+ using a 20-byte (4.1+) scramble
+*/
+
+static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
+{
+ int pkt_len;
+ uchar *pkt;
+
+ if (((MCPVIO_EXT *)vio)->mysql_change_user)
+ {
+ /*
+ in mysql_change_user() the client sends the first packet.
+ we use the old scramble.
+ */
+ pkt= (uchar*)mysql->scramble;
+ pkt_len= SCRAMBLE_LENGTH + 1;
+ }
+ else
+ {
+ /* read the scramble */
+ if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
+ return CR_ERROR;
+
+ if (pkt_len != SCRAMBLE_LENGTH + 1)
+ return CR_SERVER_HANDSHAKE_ERR;
+
+ /* save it in MYSQL */
+ memcpy(mysql->scramble, pkt, SCRAMBLE_LENGTH);
+ mysql->scramble[SCRAMBLE_LENGTH] = 0;
+ }
+
+ if (mysql->passwd[0])
+ {
+ char scrambled[SCRAMBLE_LENGTH + 1];
+ scramble(scrambled, (char*)pkt, mysql->passwd);
+ if (vio->write_packet(vio, (uchar*)scrambled, SCRAMBLE_LENGTH))
+ return CR_ERROR;
+ }
+ else
+ if (vio->write_packet(vio, 0, 0)) /* no password */
+ return CR_ERROR;
+
+ return CR_OK;
+}
+
+
+/**
+ client authentication plugin that does old MySQL authentication
+ using an 8-byte (4.0-) scramble
+*/
+
+static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
+{
+ uchar *pkt;
+ int pkt_len;
+
+ if (((MCPVIO_EXT *)vio)->mysql_change_user)
+ {
+ /*
+ in mysql_change_user() the client sends the first packet.
+ we use the old scramble.
+ */
+ pkt= (uchar*)mysql->scramble;
+ pkt_len= SCRAMBLE_LENGTH_323 + 1;
+ }
+ else
+ {
+ /* read the scramble */
+ if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
+ return CR_ERROR;
+
+ if (pkt_len != SCRAMBLE_LENGTH_323 + 1 &&
+ pkt_len != SCRAMBLE_LENGTH + 1)
+ return CR_SERVER_HANDSHAKE_ERR;
+
+ /* save it in MYSQL */
+ memcpy(mysql->scramble, pkt, pkt_len);
+ mysql->scramble[pkt_len] = 0;
+ }
+
+ if (mysql->passwd[0])
+ {
+ char scrambled[SCRAMBLE_LENGTH_323 + 1];
+ scramble_323(scrambled, (char*)pkt, mysql->passwd);
+ if (vio->write_packet(vio, (uchar*)scrambled, SCRAMBLE_LENGTH_323 + 1))
+ return CR_ERROR;
+ }
+ else
+ if (vio->write_packet(vio, 0, 0)) /* no password */
+ return CR_ERROR;
+
+ return CR_OK;
+}
+
diff --git a/sql-common/client_plugin.c b/sql-common/client_plugin.c
new file mode 100644
index 00000000000..9a90e3c93fd
--- /dev/null
+++ b/sql-common/client_plugin.c
@@ -0,0 +1,447 @@
+/* Copyright (C) 2010 Sergei Golubchik and Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/**
+ @file
+
+ Support code for the client side (libmysql) plugins
+
+ Client plugins are somewhat different from server plugins, they are simpler.
+
+ They do not need to be installed or in any way explicitly loaded on the
+ client, they are loaded automatically on demand.
+ One client plugin per shared object, soname *must* match the plugin name.
+
+ There is no reference counting and no unloading either.
+*/
+
+#if _MSC_VER
+/* Silence warnings about variable 'unused' being used. */
+#define FORCE_INIT_OF_VARS 1
+#endif
+
+#include <my_global.h>
+#include "mysql.h"
+#include <my_sys.h>
+#include <m_string.h>
+#ifdef THREAD
+#include <my_pthread.h>
+#else
+#include <my_no_pthread.h>
+#endif
+
+#include <sql_common.h>
+#include "errmsg.h"
+#include <mysql/client_plugin.h>
+
+struct st_client_plugin_int {
+ struct st_client_plugin_int *next;
+ void *dlhandle;
+ struct st_mysql_client_plugin *plugin;
+};
+
+static my_bool initialized= 0;
+static MEM_ROOT mem_root;
+
+#define plugin_declarations_sym "_mysql_client_plugin_declaration_"
+
+static uint plugin_version[MYSQL_CLIENT_MAX_PLUGINS]=
+{
+ 0, /* these two are taken by Connector/C */
+ 0, /* these two are taken by Connector/C */
+ MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION
+};
+
+/*
+ Loaded plugins are stored in a linked list.
+ The list is append-only, the elements are added to the head (like in a stack).
+ The elements are added under a mutex, but the list can be read and traversed
+ without any mutex because once an element is added to the list, it stays
+ there. The main purpose of a mutex is to prevent two threads from
+ loading the same plugin twice in parallel.
+*/
+struct st_client_plugin_int *plugin_list[MYSQL_CLIENT_MAX_PLUGINS];
+#ifdef THREAD
+static pthread_mutex_t LOCK_load_client_plugin;
+#endif
+
+static int is_not_initialized(MYSQL *mysql, const char *name)
+{
+ if (initialized)
+ return 0;
+
+ set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD,
+ unknown_sqlstate, ER(CR_AUTH_PLUGIN_CANNOT_LOAD),
+ name, "not initialized");
+ return 1;
+}
+
+
+/**
+ finds a plugin in the list
+
+ @param name plugin name to search for
+ @param type plugin type
+
+ @note this does NOT necessarily need a mutex, take care!
+
+ @retval a pointer to a found plugin or 0
+*/
+
+static struct st_mysql_client_plugin *find_plugin(const char *name, int type)
+{
+ struct st_client_plugin_int *p;
+
+ DBUG_ASSERT(initialized);
+ DBUG_ASSERT(type >= 0 && type < MYSQL_CLIENT_MAX_PLUGINS);
+ if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS)
+ return 0;
+
+ for (p= plugin_list[type]; p; p= p->next)
+ {
+ if (strcmp(p->plugin->name, name) == 0)
+ return p->plugin;
+ }
+ return NULL;
+}
+
+
+/**
+ verifies the plugin and adds it to the list
+
+ @param mysql MYSQL structure (for error reporting)
+ @param plugin plugin to install
+ @param dlhandle a handle to the shared object (returned by dlopen)
+ or 0 if the plugin was not dynamically loaded
+ @param argc number of arguments in the 'va_list args'
+ @param args arguments passed to the plugin initialization function
+
+ @retval a pointer to an installed plugin or 0
+*/
+
+static struct st_mysql_client_plugin *
+add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle,
+ int argc, va_list args)
+{
+ const char *errmsg;
+ struct st_client_plugin_int plugin_int, *p;
+ char errbuf[1024];
+
+ DBUG_ASSERT(initialized);
+
+ plugin_int.plugin= plugin;
+ plugin_int.dlhandle= dlhandle;
+
+ if (plugin->type >= MYSQL_CLIENT_MAX_PLUGINS)
+ {
+ errmsg= "Unknown client plugin type";
+ goto err1;
+ }
+
+ if (plugin->interface_version < plugin_version[plugin->type] ||
+ (plugin->interface_version >> 8) >
+ (plugin_version[plugin->type] >> 8))
+ {
+ errmsg= "Incompatible client plugin interface";
+ goto err1;
+ }
+
+ /* Call the plugin initialization function, if any */
+ if (plugin->init && plugin->init(errbuf, sizeof(errbuf), argc, args))
+ {
+ errmsg= errbuf;
+ goto err1;
+ }
+
+ p= (struct st_client_plugin_int *)
+ memdup_root(&mem_root, &plugin_int, sizeof(plugin_int));
+
+ if (!p)
+ {
+ errmsg= "Out of memory";
+ goto err2;
+ }
+
+ safe_mutex_assert_owner(&LOCK_load_client_plugin);
+
+ p->next= plugin_list[plugin->type];
+ plugin_list[plugin->type]= p;
+
+ return plugin;
+
+err2:
+ if (plugin->deinit)
+ plugin->deinit();
+err1:
+ if (dlhandle)
+ (void)dlclose(dlhandle);
+ set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate,
+ ER(CR_AUTH_PLUGIN_CANNOT_LOAD), plugin->name,
+ errmsg);
+ return NULL;
+}
+
+
+/**
+ Loads plugins which are specified in the environment variable
+ LIBMYSQL_PLUGINS.
+
+ Multiple plugins must be separated by semicolon. This function doesn't
+ return or log an error.
+
+ The function is be called by mysql_client_plugin_init
+
+ @todo
+ Support extended syntax, passing parameters to plugins, for example
+ LIBMYSQL_PLUGINS="plugin1(param1,param2);plugin2;..."
+ or
+ LIBMYSQL_PLUGINS="plugin1=int:param1,str:param2;plugin2;..."
+*/
+
+static void load_env_plugins(MYSQL *mysql)
+{
+ char *plugs, *free_env, *s= getenv("LIBMYSQL_PLUGINS");
+
+ /* no plugins to load */
+ if (!s)
+ return;
+
+ free_env= plugs= my_strdup(s, MYF(MY_WME));
+
+ do {
+ if ((s= strchr(plugs, ';')))
+ *s= '\0';
+ mysql_load_plugin(mysql, plugs, -1, 0);
+ plugs= s + 1;
+ } while (s);
+
+ my_free(free_env, MYF(0));
+}
+
+/********** extern functions to be used by libmysql *********************/
+
+/**
+ Initializes the client plugin layer.
+
+ This function must be called before any other client plugin function.
+
+ @retval 0 successful
+ @retval != 0 error occured
+*/
+
+int mysql_client_plugin_init()
+{
+ MYSQL mysql;
+ struct st_mysql_client_plugin **builtin;
+ va_list unused;
+ LINT_INIT_STRUCT(unused);
+
+ if (initialized)
+ return 0;
+
+ bzero(&mysql, sizeof(mysql)); /* dummy mysql for set_mysql_extended_error */
+
+ pthread_mutex_init(&LOCK_load_client_plugin, MY_MUTEX_INIT_SLOW);
+ init_alloc_root(&mem_root, 128, 128);
+
+ bzero(&plugin_list, sizeof(plugin_list));
+
+ initialized= 1;
+
+ pthread_mutex_lock(&LOCK_load_client_plugin);
+
+ for (builtin= mysql_client_builtins; *builtin; builtin++)
+ add_plugin(&mysql, *builtin, 0, 0, unused);
+
+ pthread_mutex_unlock(&LOCK_load_client_plugin);
+
+ load_env_plugins(&mysql);
+
+ return 0;
+}
+
+
+/**
+ Deinitializes the client plugin layer.
+
+ Unloades all client plugins and frees any associated resources.
+*/
+
+void mysql_client_plugin_deinit()
+{
+ int i;
+ struct st_client_plugin_int *p;
+
+ if (!initialized)
+ return;
+
+ for (i=0; i < MYSQL_CLIENT_MAX_PLUGINS; i++)
+ for (p= plugin_list[i]; p; p= p->next)
+ {
+ if (p->plugin->deinit)
+ p->plugin->deinit();
+ if (p->dlhandle)
+ (void)dlclose(p->dlhandle);
+ }
+
+ bzero(&plugin_list, sizeof(plugin_list));
+ initialized= 0;
+ free_root(&mem_root, MYF(0));
+ pthread_mutex_destroy(&LOCK_load_client_plugin);
+}
+
+/************* public facing functions, for client consumption *********/
+
+/* see <mysql/client_plugin.h> for a full description */
+struct st_mysql_client_plugin *
+mysql_client_register_plugin(MYSQL *mysql,
+ struct st_mysql_client_plugin *plugin)
+{
+ va_list unused;
+ LINT_INIT_STRUCT(unused);
+
+ if (is_not_initialized(mysql, plugin->name))
+ return NULL;
+
+ pthread_mutex_lock(&LOCK_load_client_plugin);
+
+ /* make sure the plugin wasn't loaded meanwhile */
+ if (find_plugin(plugin->name, plugin->type))
+ {
+ set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD,
+ unknown_sqlstate, ER(CR_AUTH_PLUGIN_CANNOT_LOAD),
+ plugin->name, "it is already loaded");
+ plugin= NULL;
+ }
+ else
+ plugin= add_plugin(mysql, plugin, 0, 0, unused);
+
+ pthread_mutex_unlock(&LOCK_load_client_plugin);
+ return plugin;
+}
+
+
+/* see <mysql/client_plugin.h> for a full description */
+struct st_mysql_client_plugin *
+mysql_load_plugin_v(MYSQL *mysql, const char *name, int type,
+ int argc, va_list args)
+{
+ const char *errmsg;
+ char dlpath[FN_REFLEN+1];
+ void *sym, *dlhandle;
+ struct st_mysql_client_plugin *plugin;
+
+ if (is_not_initialized(mysql, name))
+ return NULL;
+
+ pthread_mutex_lock(&LOCK_load_client_plugin);
+
+ /* make sure the plugin wasn't loaded meanwhile */
+ if (type >= 0 && find_plugin(name, type))
+ {
+ errmsg= "it is already loaded";
+ goto err;
+ }
+
+ /* Compile dll path */
+ strxnmov(dlpath, sizeof(dlpath) - 1,
+ mysql->options.extension && mysql->options.extension->plugin_dir ?
+ mysql->options.extension->plugin_dir : PLUGINDIR, "/",
+ name, SO_EXT, NullS);
+
+ /* Open new dll handle */
+ if (!(dlhandle= dlopen(dlpath, RTLD_NOW)))
+ {
+ errmsg= dlerror();
+ goto err;
+ }
+
+ if (!(sym= dlsym(dlhandle, plugin_declarations_sym)))
+ {
+ errmsg= "not a plugin";
+ (void)dlclose(dlhandle);
+ goto err;
+ }
+
+ plugin= (struct st_mysql_client_plugin*)sym;
+
+ if (type >=0 && type != plugin->type)
+ {
+ errmsg= "type mismatch";
+ goto err;
+ }
+
+ if (strcmp(name, plugin->name))
+ {
+ errmsg= "name mismatch";
+ goto err;
+ }
+
+ if (type < 0 && find_plugin(name, plugin->type))
+ {
+ errmsg= "it is already loaded";
+ goto err;
+ }
+
+ plugin= add_plugin(mysql, plugin, dlhandle, argc, args);
+
+ pthread_mutex_unlock(&LOCK_load_client_plugin);
+
+ return plugin;
+
+err:
+ pthread_mutex_unlock(&LOCK_load_client_plugin);
+ set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate,
+ ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name, errmsg);
+ return NULL;
+}
+
+
+/* see <mysql/client_plugin.h> for a full description */
+struct st_mysql_client_plugin *
+mysql_load_plugin(MYSQL *mysql, const char *name, int type, int argc, ...)
+{
+ struct st_mysql_client_plugin *p;
+ va_list args;
+ va_start(args, argc);
+ p= mysql_load_plugin_v(mysql, name, type, argc, args);
+ va_end(args);
+ return p;
+}
+
+
+/* see <mysql/client_plugin.h> for a full description */
+struct st_mysql_client_plugin *
+mysql_client_find_plugin(MYSQL *mysql, const char *name, int type)
+{
+ struct st_mysql_client_plugin *p;
+
+ if (is_not_initialized(mysql, name))
+ return NULL;
+
+ if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS)
+ {
+ set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate,
+ ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name,
+ "invalid type");
+ }
+
+ if ((p= find_plugin(name, type)))
+ return p;
+
+ /* not found, load it */
+ return mysql_load_plugin(mysql, name, type, 0);
+}
+
diff --git a/sql-common/my_time.c b/sql-common/my_time.c
index 6958c8e3517..bbac69d9ac9 100644
--- a/sql-common/my_time.c
+++ b/sql-common/my_time.c
@@ -159,7 +159,7 @@ my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date,
enum enum_mysql_timestamp_type
str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
- uint flags, int *was_cut)
+ ulong flags, int *was_cut)
{
uint field_length, UNINIT_VAR(year_length), digits, i, number_of_fields;
uint date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS];
@@ -172,14 +172,14 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
my_bool found_delimitier= 0, found_space= 0;
uint frac_pos, frac_len;
DBUG_ENTER("str_to_datetime");
- DBUG_PRINT("ENTER",("str: %.*s",length,str));
+ DBUG_PRINT("enter",("str: %.*s",length,str));
LINT_INIT(field_length);
if (flags & TIME_TIME_ONLY)
{
- enum enum_mysql_timestamp_type ret=
- str_to_time(str, length, l_time, was_cut);
+ enum enum_mysql_timestamp_type ret;
+ ret= str_to_time(str, length, l_time, flags, was_cut);
DBUG_RETURN(ret);
}
@@ -490,7 +490,8 @@ err:
*/
enum enum_mysql_timestamp_type
-str_to_time(const char *str, uint length, MYSQL_TIME *l_time, int *warning)
+str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
+ ulong fuzzydate, int *warning)
{
ulong date[5];
ulonglong value;
@@ -517,7 +518,8 @@ str_to_time(const char *str, uint length, MYSQL_TIME *l_time, int *warning)
int was_cut;
enum enum_mysql_timestamp_type
res= str_to_datetime(str, length, l_time,
- (TIME_FUZZY_DATE | TIME_DATETIME_ONLY), &was_cut);
+ (fuzzydate & ~TIME_TIME_ONLY) | TIME_DATETIME_ONLY,
+ &was_cut);
if ((int) res >= (int) MYSQL_TIMESTAMP_ERROR)
{
if (was_cut)
@@ -743,6 +745,10 @@ void my_init_time(void)
my_time.hour= (uint) l_time->tm_hour;
my_time.minute= (uint) l_time->tm_min;
my_time.second= (uint) l_time->tm_sec;
+ my_time.neg= 0;
+ my_time.second_part= 0;
+ my_time.time_type= MYSQL_TIMESTAMP_DATETIME;
+
my_system_gmt_sec(&my_time, &my_time_zone, &not_used); /* Init my_time_zone */
}
@@ -788,7 +794,7 @@ long calc_daynr(uint year,uint month,uint day)
int y= year; /* may be < 0 temporarily */
DBUG_ENTER("calc_daynr");
- if (y == 0 && month == 0 && day == 0)
+ if (y == 0 && month == 0)
DBUG_RETURN(0); /* Skip errors */
/* Cast to int to be able to handle month == 0 */
delsum= (long) (365 * y + 31 *((int) month - 1) + (int) day);
@@ -799,6 +805,7 @@ long calc_daynr(uint year,uint month,uint day)
temp=(int) ((y/100+1)*3)/4;
DBUG_PRINT("exit",("year: %d month: %d day: %d -> daynr: %ld",
y+(month <= 2),month,day,delsum+y/4-temp));
+ DBUG_ASSERT(delsum+(int) y/4-temp > 0);
DBUG_RETURN(delsum+(int) y/4-temp);
} /* calc_daynr */
@@ -1015,8 +1022,11 @@ my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone, uint *error_code)
with unsigned time_t tmp+= shift*86400L might result in a number,
larger then TIMESTAMP_MAX_VALUE, so another check will work.
*/
- if ((tmp < TIMESTAMP_MIN_VALUE) || (tmp > TIMESTAMP_MAX_VALUE))
+ if (!IS_TIME_T_VALID_FOR_TIMESTAMP(tmp))
+ {
tmp= 0;
+ *error_code= ER_WARN_DATA_OUT_OF_RANGE;
+ }
return (my_time_t) tmp;
} /* my_system_gmt_sec */
@@ -1042,14 +1052,14 @@ void set_zero_time(MYSQL_TIME *tm, enum enum_mysql_timestamp_type time_type)
number of characters written to 'to'
*/
-int my_time_to_str(const MYSQL_TIME *l_time, char *to, int digits)
+int my_time_to_str(const MYSQL_TIME *l_time, char *to, uint digits)
{
ulong day= (l_time->year || l_time->month) ? 0 : l_time->day;
if (digits == AUTO_SEC_PART_DIGITS)
digits= l_time->second_part ? TIME_SECOND_PART_DIGITS : 0;
- DBUG_ASSERT(digits >= 0 && digits <= TIME_SECOND_PART_DIGITS);
+ DBUG_ASSERT(digits <= TIME_SECOND_PART_DIGITS);
return sprintf(to,
digits ? "%s%02lu:%02u:%02u.%0*lu"
@@ -1061,16 +1071,18 @@ int my_time_to_str(const MYSQL_TIME *l_time, char *to, int digits)
int my_date_to_str(const MYSQL_TIME *l_time, char *to)
{
- return sprintf(to, "%04u-%02u-%02u",
- l_time->year, l_time->month, l_time->day);
+ return my_sprintf(to, (to, "%04u-%02u-%02u",
+ l_time->year,
+ l_time->month,
+ l_time->day));
}
-int my_datetime_to_str(const MYSQL_TIME *l_time, char *to, int digits)
+int my_datetime_to_str(const MYSQL_TIME *l_time, char *to, uint digits)
{
if (digits == AUTO_SEC_PART_DIGITS)
digits= l_time->second_part ? TIME_SECOND_PART_DIGITS : 0;
- DBUG_ASSERT(digits >= 0 && digits <= TIME_SECOND_PART_DIGITS);
+ DBUG_ASSERT(digits <= TIME_SECOND_PART_DIGITS);
return sprintf(to,
digits ? "%04u-%02u-%02u %02u:%02u:%02u.%0*lu"
@@ -1088,11 +1100,14 @@ int my_datetime_to_str(const MYSQL_TIME *l_time, char *to, int digits)
SYNOPSIS
my_TIME_to_string()
+ RETURN
+ length of string
+
NOTE
The string must have at least MAX_DATE_STRING_REP_LENGTH bytes reserved.
*/
-int my_TIME_to_str(const MYSQL_TIME *l_time, char *to, int digits)
+int my_TIME_to_str(const MYSQL_TIME *l_time, char *to, uint digits)
{
switch (l_time->time_type) {
case MYSQL_TIMESTAMP_DATETIME:
@@ -1144,7 +1159,6 @@ longlong number_to_datetime(longlong nr, ulong sec_part, MYSQL_TIME *time_res,
long part1,part2;
*was_cut= 0;
- bzero((char*) time_res, sizeof(*time_res));
time_res->time_type=MYSQL_TIMESTAMP_DATE;
if (nr == 0 || nr >= 10000101000000LL || sec_part)
@@ -1198,6 +1212,7 @@ longlong number_to_datetime(longlong nr, ulong sec_part, MYSQL_TIME *time_res,
time_res->minute=(int) part2 / 100;
time_res->second=(int) part2 % 100;
time_res->second_part= sec_part;
+ time_res->neg= 0;
if (time_res->year <= 9999 && time_res->month <= 12 &&
time_res->day <= 31 && time_res->hour <= 23 &&
@@ -1207,11 +1222,18 @@ longlong number_to_datetime(longlong nr, ulong sec_part, MYSQL_TIME *time_res,
return nr;
/* Don't want to have was_cut get set if NO_ZERO_DATE was violated. */
- if (!nr && (flags & TIME_NO_ZERO_DATE))
- return LL(-1);
+ if (nr || !(flags & TIME_NO_ZERO_DATE))
+ *was_cut= 1;
+ return LL(-1);
err:
- *was_cut= 1;
+ {
+ /* reset everything except time_type */
+ enum enum_mysql_timestamp_type save= time_res->time_type;
+ bzero((char*) time_res, sizeof(*time_res));
+ time_res->time_type= save; /* Restore range */
+ *was_cut= 1; /* Found invalid date */
+ }
return LL(-1);
}
@@ -1224,18 +1246,28 @@ longlong number_to_datetime(longlong nr, ulong sec_part, MYSQL_TIME *time_res,
modified to fit in the valid range. Otherwise 0.
@details
- Takes a number in the [-]HHHMMSS.uuuuuu format.
-
- number_to_datetime() cannot take a double, because double precision is not
- enough for YYYYMMDDHHMMSS.uuuuuu
+ Takes a number in the [-]HHHMMSS.uuuuuu,
+ YYMMDDHHMMSS.uuuuuu, or in the YYYYMMDDHHMMSS.uuuuuu formats.
@return
0 time value is valid, but was possibly truncated
- 1 time value is invalid
+ -1 time value is invalid
*/
int number_to_time(my_bool neg, longlong nr, ulong sec_part,
MYSQL_TIME *ltime, int *was_cut)
{
+ if (nr > 9999999 && neg == 0)
+ {
+ if (number_to_datetime(nr, sec_part, ltime,
+ TIME_FUZZY_DATE | TIME_INVALID_DATES, was_cut) < 0)
+ return -1;
+
+ ltime->year= ltime->month= ltime->day= 0;
+ ltime->time_type= MYSQL_TIMESTAMP_TIME;
+ *was_cut= MYSQL_TIME_WARN_TRUNCATED;
+ return 0;
+ }
+
*was_cut= 0;
ltime->year= ltime->month= ltime->day= 0;
ltime->time_type= MYSQL_TIMESTAMP_TIME;
@@ -1257,7 +1289,7 @@ int number_to_time(my_bool neg, longlong nr, ulong sec_part,
return 0;
*was_cut= MYSQL_TIME_WARN_TRUNCATED;
- return 1;
+ return -1;
}