diff options
author | Ondřej Surý <ondrej@sury.org> | 2010-10-21 08:52:46 +0200 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2010-10-21 08:52:46 +0200 |
commit | 01fcdff3849c3691d9aaeaab735846ab6d8895ca (patch) | |
tree | 6460876d356113fa7053df36f2aa00baa7db24a9 /ext/mysqlnd/mysqlnd.c | |
parent | 855a09f4eded707941180c9d90acd17c25e29447 (diff) | |
download | php-upstream/5.3.3.tar.gz |
Imported Upstream version 5.3.3upstream/5.3.3
Diffstat (limited to 'ext/mysqlnd/mysqlnd.c')
-rw-r--r-- | ext/mysqlnd/mysqlnd.c | 763 |
1 files changed, 487 insertions, 276 deletions
diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index 31fb47ae8..d0f2c1c20 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd.c 294543 2010-02-04 20:28:55Z johannes $ */ +/* $Id: mysqlnd.c 300635 2010-06-21 15:32:26Z andrey $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" @@ -30,9 +30,6 @@ /* for php_get_current_user() */ #include "ext/standard/basic_functions.h" -/* the server doesn't support 4byte utf8, but let's make it forward compatible */ -#define MYSQLND_MAX_ALLOWED_USER_LEN 256 /* 64 char * 4byte */ -#define MYSQLND_MAX_ALLOWED_DB_LEN 256 /* 64 char * 4byte */ /* TODO : - Don't bind so tightly the metadata with the result set. This means @@ -57,15 +54,18 @@ extern MYSQLND_CHARSET *mysqlnd_charsets; -PHPAPI const char * const mysqlnd_old_passwd = "mysqlnd cannot connect to MySQL 4.1+ using old authentication"; +PHPAPI const char * const mysqlnd_old_passwd = "mysqlnd cannot connect to MySQL 4.1+ using the old insecure authentication. " +"Please use an administration tool to reset your password with the command SET PASSWORD = PASSWORD('your_existing_password'). This will " +"store a new, and more secure, hash value in mysql.user. If this user is used in other scripts executed by PHP 5.2 or earlier you might need to remove the old-passwords " +"flag from your my.cnf file"; + PHPAPI const char * const mysqlnd_server_gone = "MySQL server has gone away"; PHPAPI const char * const mysqlnd_out_of_sync = "Commands out of sync; you can't run this command now"; +PHPAPI const char * const mysqlnd_out_of_memory = "Out of memory"; PHPAPI MYSQLND_STATS *mysqlnd_global_stats = NULL; static zend_bool mysqlnd_library_initted = FALSE; -static enum_func_status mysqlnd_send_close(MYSQLND * conn TSRMLS_DC); - static struct st_mysqlnd_conn_methods *mysqlnd_conn_methods; /* {{{ mysqlnd_library_end */ @@ -82,7 +82,7 @@ PHPAPI void mysqlnd_library_end(TSRMLS_D) /* {{{ mysqlnd_conn::free_options */ static void -MYSQLND_METHOD(mysqlnd_conn, free_options)(MYSQLND *conn TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, free_options)(MYSQLND * conn TSRMLS_DC) { zend_bool pers = conn->persistent; @@ -107,32 +107,13 @@ MYSQLND_METHOD(mysqlnd_conn, free_options)(MYSQLND *conn TSRMLS_DC) mnd_pefree(conn->options.cfg_section, pers); conn->options.cfg_section = NULL; } - if (conn->options.ssl_key) { - mnd_pefree(conn->options.ssl_key, pers); - conn->options.ssl_key = NULL; - } - if (conn->options.ssl_cert) { - mnd_pefree(conn->options.ssl_cert, pers); - conn->options.ssl_cert = NULL; - } - if (conn->options.ssl_ca) { - mnd_pefree(conn->options.ssl_ca, pers); - conn->options.ssl_ca = NULL; - } - if (conn->options.ssl_capath) { - mnd_pefree(conn->options.ssl_capath, pers); - conn->options.ssl_capath = NULL; - } - if (conn->options.ssl_cipher) { - mnd_pefree(conn->options.ssl_cipher, pers); - conn->options.ssl_cipher = NULL; - } } +/* }}} */ /* {{{ mysqlnd_conn::free_contents */ static void -MYSQLND_METHOD(mysqlnd_conn, free_contents)(MYSQLND *conn TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, free_contents)(MYSQLND * conn TSRMLS_DC) { zend_bool pers = conn->persistent; @@ -140,12 +121,13 @@ MYSQLND_METHOD(mysqlnd_conn, free_contents)(MYSQLND *conn TSRMLS_DC) mysqlnd_local_infile_default(conn); if (conn->current_result) { - conn->current_result->m.free_result_contents(conn->current_result TSRMLS_CC); - mnd_efree(conn->current_result); + conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC); conn->current_result = NULL; } - conn->net->m.free_contents(conn->net TSRMLS_CC); + if (conn->net) { + conn->net->m.free_contents(conn->net TSRMLS_CC); + } DBG_INF("Freeing memory of members"); @@ -208,7 +190,7 @@ MYSQLND_METHOD(mysqlnd_conn, free_contents)(MYSQLND *conn TSRMLS_DC) /* {{{ mysqlnd_conn::dtor */ static void -MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND *conn TSRMLS_DC) +MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND * conn TSRMLS_DC) { DBG_ENTER("mysqlnd_conn::dtor"); DBG_INF_FMT("conn=%llu", conn->thread_id); @@ -241,22 +223,26 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND *conn TSRMLS_DC) /* {{{ mysqlnd_conn::simple_command_handle_response */ static enum_func_status -MYSQLND_METHOD(mysqlnd_conn, simple_command_handle_response)(MYSQLND *conn, enum mysqlnd_packet_type ok_packet, - zend_bool silent, enum php_mysqlnd_server_command command, - zend_bool ignore_upsert_status TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, simple_command_handle_response)(MYSQLND * conn, enum mysqlnd_packet_type ok_packet, + zend_bool silent, enum php_mysqlnd_server_command command, + zend_bool ignore_upsert_status TSRMLS_DC) { - enum_func_status ret; + enum_func_status ret = FAIL; DBG_ENTER("mysqlnd_conn::simple_command_handle_response"); - DBG_INF_FMT("silent=%d packet=%d command=%s", silent, ok_packet, mysqlnd_command_to_text[command]); + DBG_INF_FMT("silent=%u packet=%u command=%s", silent, ok_packet, mysqlnd_command_to_text[command]); switch (ok_packet) { case PROT_OK_PACKET:{ MYSQLND_PACKET_OK * ok_response = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC); + if (!ok_response) { + SET_OOM_ERROR(conn->error_info); + break; + } if (FAIL == (ret = PACKET_READ(ok_response, conn))) { if (!silent) { DBG_ERR_FMT("Error while reading %s's OK packet", mysqlnd_command_to_text[command]); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's OK packet. PID=%d", + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's OK packet. PID=%u", mysqlnd_command_to_text[command], getpid()); } } else { @@ -294,25 +280,27 @@ MYSQLND_METHOD(mysqlnd_conn, simple_command_handle_response)(MYSQLND *conn, enum } case PROT_EOF_PACKET:{ MYSQLND_PACKET_EOF * ok_response = conn->protocol->m.get_eof_packet(conn->protocol, FALSE TSRMLS_CC); + if (!ok_response) { + SET_OOM_ERROR(conn->error_info); + break; + } if (FAIL == (ret = PACKET_READ(ok_response, conn))) { SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet"); if (!silent) { DBG_ERR_FMT("Error while reading %s's EOF packet", mysqlnd_command_to_text[command]); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's EOF packet. PID=%d", - mysqlnd_command_to_text[command], getpid()); + mysqlnd_command_to_text[command], getpid()); } } else if (0xFF == ok_response->field_count) { /* The server signalled error. Set the error */ SET_CLIENT_ERROR(conn->error_info, ok_response->error_no, ok_response->sqlstate, ok_response->error); SET_ERROR_AFF_ROWS(conn); } else if (0xFE != ok_response->field_count) { - SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, - "Malformed packet"); + SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet"); if (!silent) { DBG_ERR_FMT("EOF packet expected, field count wasn't 0xFE but 0x%2X", ok_response->field_count); - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "EOF packet expected, field count wasn't 0xFE but 0x%2X", + php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet expected, field count wasn't 0xFE but 0x%2X", ok_response->field_count); } } else { @@ -322,11 +310,8 @@ MYSQLND_METHOD(mysqlnd_conn, simple_command_handle_response)(MYSQLND *conn, enum break; } default: - ret = FAIL; - SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, - "Malformed packet"); - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Wrong response packet %d passed to the function", - ok_packet); + SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet"); + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Wrong response packet %u passed to the function", ok_packet); break; } DBG_INF(ret == PASS ? "PASS":"FAIL"); @@ -337,7 +322,7 @@ MYSQLND_METHOD(mysqlnd_conn, simple_command_handle_response)(MYSQLND *conn, enum /* {{{ mysqlnd_conn::simple_command */ static enum_func_status -MYSQLND_METHOD(mysqlnd_conn, simple_command)(MYSQLND *conn, enum php_mysqlnd_server_command command, +MYSQLND_METHOD(mysqlnd_conn, simple_command)(MYSQLND * conn, enum php_mysqlnd_server_command command, const char * const arg, size_t arg_len, enum mysqlnd_packet_type ok_packet, zend_bool silent, zend_bool ignore_upsert_status TSRMLS_DC) { @@ -345,7 +330,7 @@ MYSQLND_METHOD(mysqlnd_conn, simple_command)(MYSQLND *conn, enum php_mysqlnd_ser MYSQLND_PACKET_COMMAND * cmd_packet; DBG_ENTER("mysqlnd_conn::simple_command"); - DBG_INF_FMT("command=%s ok_packet=%d silent=%d", mysqlnd_command_to_text[command], ok_packet, silent); + DBG_INF_FMT("command=%s ok_packet=%u silent=%u", mysqlnd_command_to_text[command], ok_packet, silent); switch (CONN_GET_STATE(conn)) { case CONN_READY: @@ -355,9 +340,8 @@ MYSQLND_METHOD(mysqlnd_conn, simple_command)(MYSQLND *conn, enum php_mysqlnd_ser DBG_ERR("Server is gone"); DBG_RETURN(FAIL); default: - SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, - mysqlnd_out_of_sync); - DBG_ERR("Command out of sync"); + SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); + DBG_ERR_FMT("Command out of sync. State=%u", CONN_GET_STATE(conn)); DBG_RETURN(FAIL); } @@ -369,6 +353,11 @@ MYSQLND_METHOD(mysqlnd_conn, simple_command)(MYSQLND *conn, enum php_mysqlnd_ser SET_EMPTY_ERROR(conn->error_info); cmd_packet = conn->protocol->m.get_command_packet(conn->protocol, FALSE TSRMLS_CC); + if (!cmd_packet) { + SET_OOM_ERROR(conn->error_info); + DBG_RETURN(FAIL); + } + cmd_packet->command = command; if (arg && arg_len) { cmd_packet->argument = arg; @@ -411,7 +400,7 @@ MYSQLND_METHOD(mysqlnd_conn, set_server_option)(MYSQLND * const conn, enum_mysql /* {{{ _mysqlnd_restart_psession */ -PHPAPI void _mysqlnd_restart_psession(MYSQLND *conn TSRMLS_DC) +PHPAPI void _mysqlnd_restart_psession(MYSQLND * conn TSRMLS_DC) { DBG_ENTER("_mysqlnd_restart_psession"); conn->m->restart_psession(conn TSRMLS_CC); @@ -421,7 +410,7 @@ PHPAPI void _mysqlnd_restart_psession(MYSQLND *conn TSRMLS_DC) /* {{{ _mysqlnd_end_psession */ -PHPAPI void _mysqlnd_end_psession(MYSQLND *conn TSRMLS_DC) +PHPAPI void _mysqlnd_end_psession(MYSQLND * conn TSRMLS_DC) { DBG_ENTER("_mysqlnd_end_psession"); conn->m->end_psession(conn TSRMLS_CC); @@ -455,9 +444,126 @@ MYSQLND_METHOD(mysqlnd_conn, end_psession)(MYSQLND * conn TSRMLS_DC) /* }}} */ +#define MYSQLND_ASSEBLED_PACKET_MAX_SIZE 3UL*1024UL*1024UL*1024UL +/* {{{ mysqlnd_connect_run_authentication */ +static enum_func_status +mysqlnd_connect_run_authentication( + MYSQLND * conn, + const char * const user, + const char * const passwd, + const char * const db, + size_t db_len, + const MYSQLND_PACKET_GREET * const greet_packet, + const MYSQLND_OPTIONS * const options, + unsigned long mysql_flags + TSRMLS_DC) +{ + const MYSQLND_CHARSET * charset = NULL; + enum_func_status ret = FAIL; + MYSQLND_PACKET_AUTH * auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC); + MYSQLND_PACKET_OK * ok_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC); + + DBG_ENTER("mysqlnd_connect_run_authentication"); + + if (!auth_packet || !ok_packet) { + SET_OOM_ERROR(conn->error_info); + goto err; + } + +#ifdef MYSQLND_SSL_SUPPORTED + if ((greet_packet->server_capabilities & CLIENT_SSL) && (mysql_flags & CLIENT_SSL)) { + auth_packet->send_half_packet = TRUE; + } +#endif + auth_packet->user = user; + auth_packet->password = passwd; + + if (options->charset_name && (charset = mysqlnd_find_charset_name(options->charset_name))) { + auth_packet->charset_no = charset->nr; + } else { +#if PHP_MAJOR_VERSION >= 6 + auth_packet->charset_no = 200;/* utf8 - swedish collation, check mysqlnd_charset.c */ +#else + auth_packet->charset_no = greet_packet->charset_no; +#endif + } + auth_packet->db = db; + auth_packet->db_len = db_len; + auth_packet->max_packet_size= MYSQLND_ASSEBLED_PACKET_MAX_SIZE; + auth_packet->client_flags= mysql_flags; + + conn->scramble = auth_packet->server_scramble_buf = mnd_pemalloc(SCRAMBLE_LENGTH, conn->persistent); + if (!conn->scramble) { + SET_OOM_ERROR(conn->error_info); + goto err; + } + memcpy(auth_packet->server_scramble_buf, greet_packet->scramble_buf, SCRAMBLE_LENGTH); + + 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 err; + } + +#ifdef MYSQLND_SSL_SUPPORTED + if (auth_packet->send_half_packet) { + zend_bool verify = mysql_flags & CLIENT_SSL_VERIFY_SERVER_CERT? TRUE:FALSE; + DBG_INF("Switching to SSL"); + + conn->net->m.set_client_option(conn->net, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (const char *) &verify TSRMLS_CC); + + if (FAIL == conn->net->m.enable_ssl(conn->net TSRMLS_CC)) { + goto err; + } + + auth_packet->send_half_packet = FALSE; + 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 err; + } + } +#endif + + + if (FAIL == PACKET_READ(ok_packet, conn) || ok_packet->field_count >= 0xFE) { + if (ok_packet->field_count == 0xFE) { + /* old authentication with new server !*/ + DBG_ERR(mysqlnd_old_passwd); + SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd); + } else if (ok_packet->field_count == 0xFF) { + if (ok_packet->sqlstate[0]) { + strlcpy(conn->error_info.sqlstate, ok_packet->sqlstate, sizeof(conn->error_info.sqlstate)); + DBG_ERR_FMT("ERROR:%u [SQLSTATE:%s] %s", ok_packet->error_no, ok_packet->sqlstate, ok_packet->error); + } + conn->error_info.error_no = ok_packet->error_no; + strlcpy(conn->error_info.error, ok_packet->error, sizeof(conn->error_info.error)); + } + goto err; + } + + SET_NEW_MESSAGE(conn->last_message, conn->last_message_len, + ok_packet->message, ok_packet->message_len, + conn->persistent); + conn->charset = mysqlnd_find_charset_nr(auth_packet->charset_no); + ret = PASS; +err: + PACKET_FREE(auth_packet); + PACKET_FREE(ok_packet); + DBG_RETURN(ret); +} +/* }}} */ + + +#define MYSQLND_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_TRANSACTIONS | \ + CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | \ + CLIENT_MULTI_RESULTS) + + + /* {{{ mysqlnd_conn::connect */ static enum_func_status -MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND *conn, +MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn, const char *host, const char *user, const char *passwd, unsigned int passwd_len, const char *db, unsigned int db_len, @@ -466,32 +572,27 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND *conn, unsigned int mysql_flags TSRMLS_DC) { - char *transport = NULL, *errstr = NULL; - int transport_len, errcode = 0, host_len; - zend_bool self_alloced = FALSE; + char *errstr = NULL; + int errcode = 0, host_len; zend_bool unix_socket = FALSE; - const MYSQLND_CHARSET * charset; zend_bool reconnect = FALSE; zend_bool saved_compression = FALSE; MYSQLND_PACKET_GREET * greet_packet = NULL; - MYSQLND_PACKET_AUTH * auth_packet = NULL; - MYSQLND_PACKET_OK * ok_packet = NULL; DBG_ENTER("mysqlnd_conn::connect"); - DBG_INF_FMT("host=%s user=%s db=%s port=%d flags=%d persistent=%d state=%d", + DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u persistent=%u state=%u", host?host:"", user?user:"", db?db:"", port, mysql_flags, conn? conn->persistent:0, conn? CONN_GET_STATE(conn):-1); if (conn && CONN_GET_STATE(conn) > CONN_ALLOCED && CONN_GET_STATE(conn) ) { - DBG_INF_FMT("state=%d", CONN_GET_STATE(conn)); DBG_INF("Connecting on a connected handle."); if (CONN_GET_STATE(conn) < CONN_QUIT_SENT) { MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CLOSE_IMPLICIT); reconnect = TRUE; - mysqlnd_send_close(conn TSRMLS_CC); + conn->m->send_close(conn TSRMLS_CC); } conn->m->free_contents(conn TSRMLS_CC); @@ -514,50 +615,63 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND *conn, if (!host || !host[0]) { host = "localhost"; } - if (!user || !user[0]) { - user = php_get_current_user(); + if (!user) { + DBG_INF_FMT("no user given, using empty string"); + user = ""; } if (!passwd) { + DBG_INF_FMT("no password given, using empty string"); passwd = ""; passwd_len = 0; } if (!db) { + DBG_INF_FMT("no db given, using empty string"); db = ""; db_len = 0; } - if (!port && !socket) { - port = 3306; - } + host_len = strlen(host); + { + char * transport = NULL; + int transport_len; #ifndef PHP_WIN32 - if (host_len == sizeof("localhost") - 1 && !strncasecmp(host, "localhost", host_len)) { - if (!socket) { - socket = "/tmp/mysql.sock"; - } - transport_len = spprintf(&transport, 0, "unix://%s", socket); - unix_socket = TRUE; - } else + if (host_len == sizeof("localhost") - 1 && !strncasecmp(host, "localhost", host_len)) { + DBG_INF_FMT("socket=%s", socket? socket:"n/a"); + if (!socket) { + socket = "/tmp/mysql.sock"; + } + transport_len = spprintf(&transport, 0, "unix://%s", socket); + unix_socket = TRUE; + } else #endif - { - transport_len = spprintf(&transport, 0, "tcp://%s:%d", host, port); + { + if (!port) { + port = 3306; + } + transport_len = spprintf(&transport, 0, "tcp://%s:%u", host, port); + } + if (!transport) { + SET_OOM_ERROR(conn->error_info); + goto err; /* OOM */ + } + DBG_INF_FMT("transport=%s", transport); + conn->scheme = mnd_pestrndup(transport, transport_len, conn->persistent); + conn->scheme_len = transport_len; + efree(transport); /* allocated by spprintf */ + transport = NULL; + if (!conn->scheme) { + goto err; /* OOM */ + } } - DBG_INF_FMT("transport=%s", transport); - greet_packet = conn->protocol->m.get_greet_packet(conn->protocol, FALSE TSRMLS_CC); - auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC); - ok_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC); - - if (conn->persistent) { - conn->scheme = pestrndup(transport, transport_len, 1); - mnd_efree(transport); - } else { - conn->scheme = transport; + if (!greet_packet) { + SET_OOM_ERROR(conn->error_info); + goto err; /* OOM */ } - conn->scheme_len = transport_len; - DBG_INF(conn->scheme); - if (FAIL == conn->net->m.connect(conn->net, conn->scheme, transport_len, conn->persistent, &errstr, &errcode TSRMLS_CC)) { - goto err; + + if (FAIL == conn->net->m.connect(conn->net, conn->scheme, conn->scheme_len, conn->persistent, &errstr, &errcode TSRMLS_CC)) { + goto err; } DBG_INF_FMT("stream=%p", conn->net->stream); @@ -567,7 +681,7 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND *conn, php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading greeting packet. PID=%d", getpid()); goto err; } else if (greet_packet->error_no) { - DBG_ERR_FMT("errorno=%d error=%s", greet_packet->error_no, greet_packet->error); + DBG_ERR_FMT("errorno=%u error=%s", greet_packet->error_no, greet_packet->error); SET_CLIENT_ERROR(conn->error_info, greet_packet->error_no, greet_packet->sqlstate, greet_packet->error); goto err; } else if (greet_packet->pre41) { @@ -581,64 +695,46 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND *conn, conn->thread_id = greet_packet->thread_id; conn->protocol_version = greet_packet->protocol_version; - conn->server_version = pestrdup(greet_packet->server_version, conn->persistent); + conn->server_version = mnd_pestrdup(greet_packet->server_version, conn->persistent); conn->greet_charset = mysqlnd_find_charset_nr(greet_packet->charset_no); /* we allow load data local infile by default */ - mysql_flags |= CLIENT_LOCAL_FILES | CLIENT_PS_MULTI_RESULTS; + mysql_flags |= CLIENT_LOCAL_FILES | CLIENT_PS_MULTI_RESULTS; + mysql_flags |= MYSQLND_CAPABILITIES; + + if (db) { + mysql_flags |= CLIENT_CONNECT_WITH_DB; + } + + if (PG(open_basedir) && strlen(PG(open_basedir))) { + mysql_flags ^= CLIENT_LOCAL_FILES; + } + #ifndef MYSQLND_COMPRESSION_ENABLED if (mysql_flags & CLIENT_COMPRESS) { mysql_flags &= ~CLIENT_COMPRESS; } #endif - - auth_packet->user = user; - auth_packet->password = passwd; - - if (conn->options.charset_name && - (charset = mysqlnd_find_charset_name(conn->options.charset_name))) - { - auth_packet->charset_no = charset->nr; - } else { -#if PHP_MAJOR_VERSION >= 6 - auth_packet->charset_no = 200;/* utf8 - swedish collation, check mysqlnd_charset.c */ +#ifndef MYSQLND_SSL_SUPPORTED + if (mysql_flags & CLIENT_SSL) { + mysql_flags &= ~CLIENT_SSL; + } #else - auth_packet->charset_no = greet_packet->charset_no; -#endif + if (conn->net->options.ssl_key || conn->net->options.ssl_cert || + conn->net->options.ssl_ca || conn->net->options.ssl_capath || conn->net->options.ssl_cipher) + { + mysql_flags |= CLIENT_SSL; } - auth_packet->db = db; - auth_packet->db_len = db_len; - auth_packet->max_packet_size= 3UL*1024UL*1024UL*1024UL; - auth_packet->client_flags= mysql_flags; +#endif - conn->scramble = auth_packet->server_scramble_buf = mnd_pemalloc(SCRAMBLE_LENGTH, conn->persistent); - memcpy(auth_packet->server_scramble_buf, greet_packet->scramble_buf, SCRAMBLE_LENGTH); - 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); + if (FAIL == mysqlnd_connect_run_authentication(conn, user, passwd, db, db_len, greet_packet, &conn->options, mysql_flags TSRMLS_CC)) { goto err; } - if (FAIL == PACKET_READ(ok_packet, conn) || ok_packet->field_count >= 0xFE) { - if (ok_packet->field_count == 0xFE) { - /* old authentication with new server !*/ - DBG_ERR(mysqlnd_old_passwd); - SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd); - } else if (ok_packet->field_count == 0xFF) { - if (ok_packet->sqlstate[0]) { - if (!self_alloced) { - strlcpy(conn->error_info.sqlstate, ok_packet->sqlstate, sizeof(conn->error_info.sqlstate)); - } - DBG_ERR_FMT("ERROR:%d [SQLSTATE:%s] %s", ok_packet->error_no, ok_packet->sqlstate, ok_packet->error); - } - if (!self_alloced) { - conn->error_info.error_no = ok_packet->error_no; - strlcpy(conn->error_info.error, ok_packet->error, sizeof(conn->error_info.error)); - } - } - } else { + { CONN_SET_STATE(conn, CONN_READY); - if (!self_alloced && saved_compression) { + + if (saved_compression) { conn->net->compressed = TRUE; } /* @@ -648,108 +744,113 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND *conn, */ conn->net->compressed = mysql_flags & CLIENT_COMPRESS? TRUE:FALSE; - conn->user = pestrdup(user, conn->persistent); + conn->user = mnd_pestrdup(user, conn->persistent); conn->user_len = strlen(conn->user); - conn->passwd = pestrndup(passwd, passwd_len, conn->persistent); + conn->passwd = mnd_pestrndup(passwd, passwd_len, conn->persistent); conn->passwd_len = passwd_len; conn->port = port; - conn->connect_or_select_db = pestrndup(db, db_len, conn->persistent); + conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent); conn->connect_or_select_db_len = db_len; - if (!unix_socket) { - char *p; + if (!conn->user || !conn->passwd || !conn->connect_or_select_db) { + SET_OOM_ERROR(conn->error_info); + goto err; /* OOM */ + } - conn->host = pestrdup(host, conn->persistent); + if (!unix_socket) { + conn->host = mnd_pestrdup(host, conn->persistent); + if (!conn->host) { + SET_OOM_ERROR(conn->error_info); + goto err; /* OOM */ + } conn->host_len = strlen(conn->host); - spprintf(&p, 0, "%s via TCP/IP", conn->host); - if (conn->persistent) { - conn->host_info = pestrdup(p, 1); - mnd_efree(p); - } else { - conn->host_info = p; + { + char *p; + spprintf(&p, 0, "%s via TCP/IP", conn->host); + if (!p) { + SET_OOM_ERROR(conn->error_info); + goto err; /* OOM */ + } + conn->host_info = mnd_pestrdup(p, conn->persistent); + efree(p); /* allocated by spprintf */ + if (!conn->host_info) { + SET_OOM_ERROR(conn->error_info); + goto err; /* OOM */ + } } } else { - conn->unix_socket = pestrdup(socket, conn->persistent); + conn->unix_socket = mnd_pestrdup(socket, conn->persistent); + conn->host_info = mnd_pestrdup("Localhost via UNIX socket", conn->persistent); + if (!conn->unix_socket || !conn->host_info) { + SET_OOM_ERROR(conn->error_info); + goto err; /* OOM */ + } conn->unix_socket_len = strlen(conn->unix_socket); - conn->host_info = pestrdup("Localhost via UNIX socket", conn->persistent); } - conn->client_flag = auth_packet->client_flags; - conn->max_packet_size = auth_packet->max_packet_size; + conn->client_flag = mysql_flags; + conn->max_packet_size = MYSQLND_ASSEBLED_PACKET_MAX_SIZE; /* todo: check if charset is available */ - conn->charset = mysqlnd_find_charset_nr(auth_packet->charset_no); conn->server_capabilities = greet_packet->server_capabilities; conn->upsert_status.warning_count = 0; conn->upsert_status.server_status = greet_packet->server_status; conn->upsert_status.affected_rows = 0; - SET_NEW_MESSAGE(conn->last_message, conn->last_message_len, - ok_packet->message, ok_packet->message_len, - conn->persistent); SET_EMPTY_ERROR(conn->error_info); mysqlnd_local_infile_default(conn); - { - unsigned int buf_size = MYSQLND_G(net_cmd_buffer_size); /* this is long, cast to unsigned int*/ - conn->m->set_client_option(conn, MYSQLND_OPT_NET_CMD_BUFFER_SIZE, (char *)&buf_size TSRMLS_CC); - } - MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_CONNECT_SUCCESS, 1, STAT_OPENED_CONNECTIONS, 1); - if (reconnect) { - MYSQLND_INC_GLOBAL_STATISTIC(STAT_RECONNECT); - } - if (conn->persistent) { - MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_PCONNECT_SUCCESS, 1, STAT_OPENED_PERSISTENT_CONNECTIONS, 1); - } - - DBG_INF_FMT("connection_id=%llu", conn->thread_id); #if PHP_MAJOR_VERSION >= 6 { unsigned int as_unicode = 1; - conn->m->set_client_option(conn, MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE, - (char *)&as_unicode TSRMLS_CC); + conn->m->set_client_option(conn, MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE, (char *)&as_unicode TSRMLS_CC); DBG_INF("unicode set"); } #endif - if (conn->options.init_commands) { - int current_command = 0; + unsigned int current_command = 0; for (; current_command < conn->options.num_commands; ++current_command) { const char * const command = conn->options.init_commands[current_command]; - MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_EXECUTED_COUNT); - if (PASS != conn->m->query(conn, command, strlen(command) TSRMLS_CC)) { - MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_FAILED_COUNT); - goto err; - } - if (conn->last_query_type == QUERY_SELECT) { - MYSQLND_RES * result = conn->m->use_result(conn TSRMLS_CC); - result->m.free_result(result, TRUE TSRMLS_CC); + if (command) { + MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_EXECUTED_COUNT); + if (PASS != conn->m->query(conn, command, strlen(command) TSRMLS_CC)) { + MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_FAILED_COUNT); + goto err; + } + if (conn->last_query_type == QUERY_SELECT) { + MYSQLND_RES * result = conn->m->use_result(conn TSRMLS_CC); + if (result) { + result->m.free_result(result, TRUE TSRMLS_CC); + } + } } } } + + MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_CONNECT_SUCCESS, 1, STAT_OPENED_CONNECTIONS, 1); + if (reconnect) { + MYSQLND_INC_GLOBAL_STATISTIC(STAT_RECONNECT); + } + if (conn->persistent) { + MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_PCONNECT_SUCCESS, 1, STAT_OPENED_PERSISTENT_CONNECTIONS, 1); + } + + DBG_INF_FMT("connection_id=%llu", conn->thread_id); + PACKET_FREE(greet_packet); - PACKET_FREE(auth_packet); - PACKET_FREE(ok_packet); DBG_RETURN(PASS); } err: PACKET_FREE(greet_packet); - PACKET_FREE(auth_packet); - PACKET_FREE(ok_packet); if (errstr) { - DBG_ERR_FMT("[%d] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme); + DBG_ERR_FMT("[%u] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme); SET_CLIENT_ERROR(conn->error_info, errcode? errcode:CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, errstr); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%d] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%u] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme); /* no mnd_ since we don't allocate it */ efree(errstr); } - if (conn->scheme) { - /* no mnd_ since we don't allocate it */ - pefree(conn->scheme, conn->persistent); - conn->scheme = NULL; - } MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_FAILURE); @@ -768,15 +869,18 @@ PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn, unsigned int mysql_flags TSRMLS_DC) { - enum_func_status ret; + enum_func_status ret = FAIL; zend_bool self_alloced = FALSE; DBG_ENTER("mysqlnd_connect"); - DBG_INF_FMT("host=%s user=%s db=%s port=%d flags=%d", host?host:"", user?user:"", db?db:"", port, mysql_flags); + DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u", host?host:"", user?user:"", db?db:"", port, mysql_flags); if (!conn) { - conn = mysqlnd_init(FALSE); self_alloced = TRUE; + if (!(conn = mysqlnd_init(FALSE))) { + /* OOM */ + DBG_RETURN(NULL); + } } ret = conn->m->connect(conn, host, user, passwd, passwd_len, db, db_len, port, socket, mysql_flags TSRMLS_CC); @@ -805,7 +909,7 @@ PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn, Still the result from the query is PASS */ static enum_func_status -MYSQLND_METHOD(mysqlnd_conn, query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, query)(MYSQLND * conn, const char * query, unsigned int query_len TSRMLS_DC) { enum_func_status ret; DBG_ENTER("mysqlnd_conn::query"); @@ -833,7 +937,7 @@ MYSQLND_METHOD(mysqlnd_conn, query)(MYSQLND *conn, const char *query, unsigned i /* {{{ mysqlnd_conn::send_query */ static enum_func_status -MYSQLND_METHOD(mysqlnd_conn, send_query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, send_query)(MYSQLND * conn, const char * query, unsigned int query_len TSRMLS_DC) { enum_func_status ret; DBG_ENTER("mysqlnd_conn::send_query"); @@ -858,7 +962,7 @@ MYSQLND_METHOD(mysqlnd_conn, reap_query)(MYSQLND * conn TSRMLS_DC) if (state <= CONN_READY || state == CONN_QUIT_SENT) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not opened, clear or has been closed"); - DBG_ERR_FMT("Connection not opened, clear or has been closed. State=%d", state); + DBG_ERR_FMT("Connection not opened, clear or has been closed. State=%u", state); DBG_RETURN(FAIL); } DBG_RETURN(conn->m->query_read_result_set_header(conn, NULL TSRMLS_CC)); @@ -1055,10 +1159,10 @@ _mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long s /* {{{ mysqlnd_conn::list_fields */ MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_conn, list_fields)(MYSQLND *conn, const char *table, const char *achtung_wild TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, list_fields)(MYSQLND * conn, const char *table, const char *achtung_wild TSRMLS_DC) { /* db + \0 + wild + \0 (for wild) */ - char buff[MYSQLND_MAX_ALLOWED_DB_LEN * 4 * 2 + 1 + 1], *p; + char buff[MYSQLND_MAX_ALLOWED_DB_LEN * 2 + 1 + 1], *p; size_t table_len, wild_len; MYSQLND_RES *result = NULL; DBG_ENTER("mysqlnd_conn::list_fields"); @@ -1066,14 +1170,16 @@ MYSQLND_METHOD(mysqlnd_conn, list_fields)(MYSQLND *conn, const char *table, cons p = buff; if (table && (table_len = strlen(table))) { - memcpy(p, table, MIN(table_len, MYSQLND_MAX_ALLOWED_DB_LEN * 4)); - p += table_len; + size_t to_copy = MIN(table_len, MYSQLND_MAX_ALLOWED_DB_LEN); + memcpy(p, table, to_copy); + p += to_copy; *p++ = '\0'; } if (achtung_wild && (wild_len = strlen(achtung_wild))) { - memcpy(p, achtung_wild, MIN(wild_len, MYSQLND_MAX_ALLOWED_DB_LEN * 4)); - p += wild_len; + size_t to_copy = MIN(wild_len, MYSQLND_MAX_ALLOWED_DB_LEN); + memcpy(p, achtung_wild, to_copy); + p += to_copy; *p++ = '\0'; } @@ -1082,12 +1188,15 @@ MYSQLND_METHOD(mysqlnd_conn, list_fields)(MYSQLND *conn, const char *table, cons FALSE, TRUE TSRMLS_CC)) { DBG_RETURN(NULL); } + /* Prepare for the worst case. MyISAM goes to 2500 BIT columns, double it for safety. */ - result = mysqlnd_result_init(5000 TSRMLS_CC); - + result = conn->m->result_init(5000, conn->persistent TSRMLS_CC); + if (!result) { + DBG_RETURN(NULL); + } if (FAIL == result->m.read_result_metadata(result, conn TSRMLS_CC)) { DBG_ERR("Error ocurred while reading metadata"); @@ -1098,6 +1207,12 @@ MYSQLND_METHOD(mysqlnd_conn, list_fields)(MYSQLND *conn, const char *table, cons result->type = MYSQLND_RES_NORMAL; result->m.fetch_row = result->m.fetch_row_normal_unbuffered; result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); + if (!result->unbuf) { + /* OOM */ + SET_OOM_ERROR(conn->error_info); + result->m.free_result(result, TRUE TSRMLS_CC); + DBG_RETURN(NULL); + } result->unbuf->eof_reached = TRUE; DBG_RETURN(result); @@ -1107,14 +1222,14 @@ MYSQLND_METHOD(mysqlnd_conn, list_fields)(MYSQLND *conn, const char *table, cons /* {{{ mysqlnd_conn::list_method */ MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_conn, list_method)(MYSQLND *conn, const char *query, const char *achtung_wild, char *par1 TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, list_method)(MYSQLND * conn, const char * query, const char *achtung_wild, char *par1 TSRMLS_DC) { char *show_query = NULL; size_t show_query_len; MYSQLND_RES *result = NULL; DBG_ENTER("mysqlnd_conn::list_method"); - DBG_INF_FMT("conn=%llu query=%s wild=%d", conn->thread_id, query, achtung_wild); + DBG_INF_FMT("conn=%llu query=%s wild=%u", conn->thread_id, query, achtung_wild); if (par1) { if (achtung_wild) { @@ -1134,7 +1249,7 @@ MYSQLND_METHOD(mysqlnd_conn, list_method)(MYSQLND *conn, const char *query, cons result = conn->m->store_result(conn TSRMLS_CC); } if (show_query != query) { - mnd_efree(show_query); + efree(show_query); /* allocated by spprintf */ } DBG_RETURN(result); } @@ -1177,6 +1292,18 @@ PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, size } /* }}} */ +/* {{{ mysqlnd_conn::ssl_set */ +void +MYSQLND_METHOD(mysqlnd_conn, ssl_set)(MYSQLND * const conn, const char * key, const char * const cert, const char * const ca, const char * const capath, const char * const cipher TSRMLS_DC) +{ + conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_KEY, key TSRMLS_CC); + conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CERT, cert TSRMLS_CC); + conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CA, ca TSRMLS_CC); + conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CAPATH, capath TSRMLS_CC); + conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CIPHER, cipher TSRMLS_CC); +} +/* }}} */ + /* {{{ mysqlnd_conn::escape_string */ static ulong @@ -1220,10 +1347,15 @@ MYSQLND_METHOD(mysqlnd_conn, select_db)(MYSQLND * const conn, const char * const SET_ERROR_AFF_ROWS(conn); if (ret == PASS) { if (conn->connect_or_select_db) { - pefree(conn->connect_or_select_db, conn->persistent); + mnd_pefree(conn->connect_or_select_db, conn->persistent); } - conn->connect_or_select_db = pestrndup(db, db_len, conn->persistent); + conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent); conn->connect_or_select_db_len = db_len; + if (!conn->connect_or_select_db) { + /* OOM */ + SET_OOM_ERROR(conn->error_info); + ret = FAIL; + } } DBG_RETURN(ret); } @@ -1246,7 +1378,7 @@ MYSQLND_METHOD(mysqlnd_conn, ping)(MYSQLND * const conn TSRMLS_DC) */ SET_ERROR_AFF_ROWS(conn); - DBG_INF_FMT("ret=%d", ret); + DBG_INF_FMT("ret=%u", ret); DBG_RETURN(ret); } /* }}} */ @@ -1254,7 +1386,7 @@ MYSQLND_METHOD(mysqlnd_conn, ping)(MYSQLND * const conn TSRMLS_DC) /* {{{ mysqlnd_conn::stat */ static enum_func_status -MYSQLND_METHOD(mysqlnd_conn, stat)(MYSQLND *conn, char **message, unsigned int * message_len TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, stat)(MYSQLND * conn, char **message, unsigned int * message_len TSRMLS_DC) { enum_func_status ret; MYSQLND_PACKET_STATS * stats_header; @@ -1267,13 +1399,17 @@ MYSQLND_METHOD(mysqlnd_conn, stat)(MYSQLND *conn, char **message, unsigned int * DBG_RETURN(FAIL); } stats_header = conn->protocol->m.get_stats_packet(conn->protocol, FALSE TSRMLS_CC); + if (!stats_header) { + SET_OOM_ERROR(conn->error_info); + DBG_RETURN(FAIL); + } + if (FAIL == (ret = PACKET_READ(stats_header, conn))) { DBG_RETURN(FAIL); } - *message = stats_header->message; + /* will be freed by Zend, thus don't use the mnd_ allocator */ + *message = estrndup(stats_header->message, stats_header->message_len); *message_len = stats_header->message_len; - /* Ownership transfer */ - stats_header->message = NULL; PACKET_FREE(stats_header); DBG_INF(*message); @@ -1284,7 +1420,7 @@ MYSQLND_METHOD(mysqlnd_conn, stat)(MYSQLND *conn, char **message, unsigned int * /* {{{ mysqlnd_conn::kill */ static enum_func_status -MYSQLND_METHOD(mysqlnd_conn, kill)(MYSQLND *conn, unsigned int pid TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, kill)(MYSQLND * conn, unsigned int pid TSRMLS_DC) { enum_func_status ret; char buff[4]; @@ -1316,7 +1452,7 @@ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, set_charset)(MYSQLND * const conn, const char * const csname TSRMLS_DC) { enum_func_status ret = PASS; - char *query; + char * query; size_t query_len; const MYSQLND_CHARSET * const charset = mysqlnd_find_charset_name(csname); @@ -1338,7 +1474,7 @@ MYSQLND_METHOD(mysqlnd_conn, set_charset)(MYSQLND * const conn, const char * con } else { conn->charset = charset; } - mnd_efree(query); + efree(query); /* allocated by spprintf */ DBG_INF(ret == PASS? "PASS":"FAIL"); DBG_RETURN(ret); @@ -1378,7 +1514,7 @@ MYSQLND_METHOD(mysqlnd_conn, shutdown)(MYSQLND * const conn, uint8_t level TSRML /* {{{ mysqlnd_send_close */ static enum_func_status -mysqlnd_send_close(MYSQLND * conn TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, send_close)(MYSQLND * const conn TSRMLS_DC) { enum_func_status ret = PASS; @@ -1458,7 +1594,7 @@ MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn, enum_connection_close_type c Close now, free_reference will try, if we are last, but that's not a problem. */ - ret = mysqlnd_send_close(conn TSRMLS_CC); + ret = conn->m->send_close(conn TSRMLS_CC); ret = conn->m->free_reference(conn TSRMLS_CC); @@ -1492,7 +1628,7 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_conn, free_reference)(MYSQLND * const conn TSRMLS This will free the object too, of course because references has reached zero. */ - ret = mysqlnd_send_close(conn TSRMLS_CC); + ret = conn->m->send_close(conn TSRMLS_CC); conn->m->dtor(conn TSRMLS_CC); } DBG_RETURN(ret); @@ -1515,7 +1651,7 @@ static void MYSQLND_METHOD_PRIVATE(mysqlnd_conn, set_state)(MYSQLND * const conn, enum mysqlnd_connection_state new_state TSRMLS_DC) { DBG_ENTER("mysqlnd_conn::set_state"); - DBG_INF_FMT("New state=%d", new_state); + DBG_INF_FMT("New state=%u", new_state); conn->state = new_state; DBG_VOID_RETURN; } @@ -1686,11 +1822,11 @@ MYSQLND_METHOD(mysqlnd_conn, next_result)(MYSQLND * const conn TSRMLS_DC) So there are no more results and we should just return FALSE, error_no has been set */ if (!conn->error_info.error_no) { - DBG_ERR_FMT("Serious error. %s::%d", __FILE__, __LINE__); + DBG_ERR_FMT("Serious error. %s::%u", __FILE__, __LINE__); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Serious error. PID=%d", getpid()); CONN_SET_STATE(conn, CONN_QUIT_SENT); } else { - DBG_INF_FMT("Error from the server : (%d) %s", conn->error_info.error_no, conn->error_info.error); + DBG_INF_FMT("Error from the server : (%u) %s", conn->error_info.error_no, conn->error_info.error); } } @@ -1761,18 +1897,21 @@ MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn, /* User could be max 16 * 3 (utf8), pass is 20 usually, db is up to 64*3 Stack space is not that expensive, so use a bit more to be protected against - stack overrungs. + buffer overflows. */ size_t user_len; - enum_func_status ret; + enum_func_status ret = FAIL; MYSQLND_PACKET_CHG_USER_RESPONSE * chg_user_resp; - char buffer[MYSQLND_MAX_ALLOWED_USER_LEN + 1 + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1]; + char buffer[MYSQLND_MAX_ALLOWED_USER_LEN + 1 + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 2 /* charset*/ ]; char *p = buffer; + const MYSQLND_CHARSET * old_cs = conn->charset; DBG_ENTER("mysqlnd_conn::change_user"); - DBG_INF_FMT("conn=%llu user=%s passwd=%s db=%s silent=%d", + DBG_INF_FMT("conn=%llu user=%s passwd=%s db=%s silent=%u", conn->thread_id, user?user:"", passwd?"***":"null", db?db:"", (silent == TRUE)?1:0 ); + SET_ERROR_AFF_ROWS(conn); + if (!user) { user = ""; } @@ -1784,7 +1923,7 @@ MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn, } /* 1. user ASCIIZ */ - user_len = MIN(strlen(user), MYSQLND_MAX_ALLOWED_DB_LEN); + user_len = MIN(strlen(user), MYSQLND_MAX_ALLOWED_USER_LEN); memcpy(p, user, user_len); p += user_len; *p++ = '\0'; @@ -1800,12 +1939,22 @@ MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn, /* 3. db ASCIIZ */ if (db[0]) { - size_t db_len = strlen(db); - memcpy(p, db, MIN(db_len, MYSQLND_MAX_ALLOWED_DB_LEN)); + size_t db_len = MIN(strlen(db), MYSQLND_MAX_ALLOWED_DB_LEN); + memcpy(p, db, db_len); p += db_len; } *p++ = '\0'; + /* + 4. request the current charset, or it will be reset to the system one. + 5.0 doesn't support it. Support added in 5.1.23 by fixing the following bug : + Bug #30472 libmysql doesn't reset charset, insert_id after succ. mysql_change_user() call + */ + if (mysqlnd_get_server_version(conn) >= 50123) { + int2store(p, conn->charset->nr); + p+=2; + } + if (PASS != conn->m->simple_command(conn, COM_CHANGE_USER, buffer, p - buffer, PROT_LAST /* we will handle the OK packet*/, silent, TRUE TSRMLS_CC)) { @@ -1813,9 +1962,12 @@ MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn, } chg_user_resp = conn->protocol->m.get_change_user_response_packet(conn->protocol, FALSE TSRMLS_CC); + if (!chg_user_resp) { + SET_OOM_ERROR(conn->error_info); + goto end; + } ret = PACKET_READ(chg_user_resp, conn); conn->error_info = chg_user_resp->error_info; - PACKET_FREE(chg_user_resp); if (conn->error_info.error_no) { ret = FAIL; @@ -1824,29 +1976,44 @@ MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn, bug#25371 mysql_change_user() triggers "packets out of sync" When it gets fixed, there should be one more check here */ - if (mysqlnd_get_server_version(conn) > 50113L && - mysqlnd_get_server_version(conn) < 50118L) - { + if (mysqlnd_get_server_version(conn) > 50113L && mysqlnd_get_server_version(conn) < 50118L) { MYSQLND_PACKET_OK * redundant_error_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC); - PACKET_READ(redundant_error_packet, conn); - PACKET_FREE(redundant_error_packet); - DBG_INF_FMT("Server is %d, buggy, sends two ERR messages", mysqlnd_get_server_version(conn)); + 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", mysqlnd_get_server_version(conn)); + } else { + SET_OOM_ERROR(conn->error_info); + } } } if (ret == PASS) { - mnd_pefree(conn->user, conn->persistent); - conn->user = pestrndup(user, user_len, conn->persistent); - mnd_pefree(conn->passwd, conn->persistent); - conn->passwd = pestrdup(passwd, conn->persistent); + if (conn->user) { + mnd_pefree(conn->user, conn->persistent); + } + conn->user = mnd_pestrndup(user, user_len, conn->persistent); + + if (conn->passwd) { + mnd_pefree(conn->passwd, conn->persistent); + } + conn->passwd = mnd_pestrdup(passwd, conn->persistent); + if (conn->last_message) { mnd_pefree(conn->last_message, conn->persistent); conn->last_message = NULL; } - conn->charset = conn->greet_charset; memset(&conn->upsert_status, 0, sizeof(conn->upsert_status)); + /* set charset for old servers */ + if (mysqlnd_get_server_version(conn) < 50123) { + ret = conn->m->set_charset(conn, old_cs->name TSRMLS_CC); + } + } 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); } - - SET_ERROR_AFF_ROWS(conn); +end: + PACKET_FREE(chg_user_resp); /* Here we should close all statements. Unbuffered queries should not be a @@ -1865,8 +2032,9 @@ MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn, const char * const value TSRMLS_DC) { + enum_func_status ret = PASS; DBG_ENTER("mysqlnd_conn::set_client_option"); - DBG_INF_FMT("conn=%llu option=%d", conn->thread_id, option); + DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option); switch (option) { #ifdef WHEN_SUPPORTED_BY_MYSQLI case MYSQL_OPT_COMPRESS: @@ -1875,10 +2043,16 @@ MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn, case MYSQL_OPT_READ_TIMEOUT: case MYSQL_OPT_WRITE_TIMEOUT: #endif + case MYSQLND_OPT_SSL_KEY: + case MYSQLND_OPT_SSL_CERT: + case MYSQLND_OPT_SSL_CA: + case MYSQLND_OPT_SSL_CAPATH: + case MYSQLND_OPT_SSL_CIPHER: + case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: case MYSQL_OPT_CONNECT_TIMEOUT: case MYSQLND_OPT_NET_CMD_BUFFER_SIZE: case MYSQLND_OPT_NET_READ_BUFFER_SIZE: - conn->net->m.set_client_option(conn->net, option, value TSRMLS_CC); + ret = conn->net->m.set_client_option(conn->net, option, value TSRMLS_CC); break; #if PHP_MAJOR_VERSION >= 6 case MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE: @@ -1900,28 +2074,48 @@ MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn, } break; case MYSQL_INIT_COMMAND: + { + char ** new_init_commands; + char * new_command; DBG_INF("MYSQL_INIT_COMMAND"); DBG_INF_FMT("command=%s", value); /* when num_commands is 0, then realloc will be effectively a malloc call, internally */ - conn->options.init_commands = mnd_perealloc(conn->options.init_commands, sizeof(char *) * (conn->options.num_commands + 1), - conn->persistent); - conn->options.init_commands[conn->options.num_commands] = pestrdup(value, conn->persistent); + /* Don't assign to conn->options.init_commands because in case of OOM we will lose the pointer and leak */ + new_init_commands = mnd_perealloc(conn->options.init_commands, sizeof(char *) * (conn->options.num_commands + 1), conn->persistent); + if (!new_init_commands) { + goto oom; + } + conn->options.init_commands = new_init_commands; + new_command = mnd_pestrdup(value, conn->persistent); + if (!new_command) { + goto oom; + } + conn->options.init_commands[conn->options.num_commands] = new_command; ++conn->options.num_commands; break; + } case MYSQL_READ_DEFAULT_FILE: case MYSQL_READ_DEFAULT_GROUP: #ifdef WHEN_SUPPORTED_BY_MYSQLI case MYSQL_SET_CLIENT_IP: case MYSQL_REPORT_DATA_TRUNCATION: - case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: #endif /* currently not supported. Todo!! */ break; case MYSQL_SET_CHARSET_NAME: + { + char * new_charset_name = mnd_pestrdup(value, conn->persistent); DBG_INF("MYSQL_SET_CHARSET_NAME"); - conn->options.charset_name = pestrdup(value, conn->persistent); + if (!new_charset_name) { + goto oom; + } + if (conn->options.charset_name) { + mnd_pefree(conn->options.charset_name, conn->persistent); + } + conn->options.charset_name = new_charset_name; DBG_INF_FMT("charset=%s", conn->options.charset_name); break; + } #ifdef WHEN_SUPPORTED_BY_MYSQLI case MYSQL_SET_CHARSET_DIR: case MYSQL_OPT_RECONNECT: @@ -1944,9 +2138,12 @@ MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn, /* not sure, todo ? */ #endif default: - DBG_RETURN(FAIL); + ret = FAIL; } - DBG_RETURN(PASS); + DBG_RETURN(ret); +oom: + SET_OOM_ERROR(conn->error_info); + DBG_RETURN(FAIL); } /* }}} */ @@ -1955,7 +2152,7 @@ MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn, static MYSQLND_RES * MYSQLND_METHOD(mysqlnd_conn, use_result)(MYSQLND * const conn TSRMLS_DC) { - MYSQLND_RES *result; + MYSQLND_RES * result; DBG_ENTER("mysqlnd_conn::use_result"); DBG_INF_FMT("conn=%llu", conn->thread_id); @@ -1974,11 +2171,14 @@ MYSQLND_METHOD(mysqlnd_conn, use_result)(MYSQLND * const conn TSRMLS_DC) MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_UNBUFFERED_SETS); - result = conn->current_result; - conn->current_result = NULL; - result->conn = conn->m->get_reference(conn TSRMLS_CC); + conn->current_result->conn = conn->m->get_reference(conn TSRMLS_CC); + result = conn->current_result->m.use_result(conn->current_result, FALSE TSRMLS_CC); - result = result->m.use_result(result, FALSE TSRMLS_CC); + if (!result) { + conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC); + } + conn->current_result = NULL; + DBG_RETURN(result); } /* }}} */ @@ -2007,10 +2207,11 @@ MYSQLND_METHOD(mysqlnd_conn, store_result)(MYSQLND * const conn TSRMLS_DC) MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS); - result = conn->current_result; + result = conn->current_result->m.store_result(conn->current_result, conn, FALSE TSRMLS_CC); + if (!result) { + conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC); + } conn->current_result = NULL; - - result = result->m.store_result(result, conn, FALSE TSRMLS_CC); DBG_RETURN(result); } /* }}} */ @@ -2031,7 +2232,7 @@ MYSQLND_METHOD(mysqlnd_conn, get_connection_stats)(const MYSQLND * const conn, MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC); -static void MYSQLND_METHOD(mysqlnd_conn, init)(MYSQLND * conn TSRMLS_DC); +static enum_func_status MYSQLND_METHOD(mysqlnd_conn, init)(MYSQLND * conn TSRMLS_DC); static MYSQLND_CLASS_METHODS_START(mysqlnd_conn) @@ -2099,22 +2300,26 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn) MYSQLND_METHOD(mysqlnd_conn, simple_command), MYSQLND_METHOD(mysqlnd_conn, simple_command_handle_response), MYSQLND_METHOD(mysqlnd_conn, restart_psession), - MYSQLND_METHOD(mysqlnd_conn, end_psession) + MYSQLND_METHOD(mysqlnd_conn, end_psession), + MYSQLND_METHOD(mysqlnd_conn, send_close), + + MYSQLND_METHOD(mysqlnd_conn, ssl_set), + mysqlnd_result_init MYSQLND_CLASS_METHODS_END; /* {{{ mysqlnd_conn::init */ -static void +static enum_func_status MYSQLND_METHOD(mysqlnd_conn, init)(MYSQLND * conn TSRMLS_DC) { DBG_ENTER("mysqlnd_conn::init"); - conn->net = mysqlnd_net_init(conn->persistent TSRMLS_CC); - conn->protocol = mysqlnd_protocol_init(conn->persistent TSRMLS_CC); mysqlnd_stats_init(&conn->stats, STAT_LAST); - SET_ERROR_AFF_ROWS(conn); - DBG_VOID_RETURN; + conn->net = mysqlnd_net_init(conn->persistent TSRMLS_CC); + conn->protocol = mysqlnd_protocol_init(conn->persistent TSRMLS_CC); + + DBG_RETURN(conn->net && conn->protocol? PASS:FAIL); } /* }}} */ @@ -2126,14 +2331,20 @@ PHPAPI MYSQLND * _mysqlnd_init(zend_bool persistent TSRMLS_DC) MYSQLND *ret = mnd_pecalloc(1, alloc_size, persistent); DBG_ENTER("mysqlnd_init"); - DBG_INF_FMT("persistent=%d", persistent); + DBG_INF_FMT("persistent=%u", persistent); + if (!ret) { + DBG_RETURN(NULL); + } ret->persistent = persistent; ret->m = mysqlnd_conn_methods; CONN_SET_STATE(ret, CONN_ALLOCED); ret->m->get_reference(ret TSRMLS_CC); - ret->m->init(ret TSRMLS_CC); + if (PASS != ret->m->init(ret TSRMLS_CC)) { + ret->m->dtor(ret TSRMLS_CC); + ret = NULL; + } DBG_RETURN(ret); } |