From 5117bf657e8ee069a74e2dfa640756e9d097f877 Mon Sep 17 00:00:00 2001 From: Andrey Hristov Date: Tue, 18 Oct 2011 14:10:44 +0000 Subject: [PATCH] move change_user closer to connect --- ext/mysqlnd/mysqlnd.c | 281 +++++++++++++++++++++--------------------- 1 file changed, 138 insertions(+), 143 deletions(-) diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index 509f0c7da9b0a..b9dec8f439f4b 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -886,6 +886,144 @@ PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn, /* }}} */ +/* {{{ mysqlnd_conn::change_user */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn, + const char *user, + const char *passwd, + const char *db, + zend_bool silent TSRMLS_DC) +{ + size_t user_len; + enum_func_status ret = FAIL; + MYSQLND_PACKET_CHG_USER_RESPONSE * chg_user_resp; + char buffer[MYSQLND_MAX_ALLOWED_USER_LEN + 1 + 1 + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 2 /* charset*/ + 2]; + 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=%u", + conn->thread_id, user?user:"", passwd?"***":"null", db?db:"", (silent == TRUE)?1:0 ); + + SET_ERROR_AFF_ROWS(conn); + + if (!user) { + user = ""; + } + if (!passwd) { + passwd = ""; + } + if (!db) { + db = ""; + } + + /* 1. user ASCIIZ */ + user_len = MIN(strlen(user), MYSQLND_MAX_ALLOWED_USER_LEN); + memcpy(p, user, user_len); + p += user_len; + *p++ = '\0'; + + /* 2. password SCRAMBLE_LENGTH followed by the scramble or \0 */ + if (passwd[0]) { + *p++ = SCRAMBLE_LENGTH; + php_mysqlnd_scramble((unsigned char *)p, conn->scramble, (unsigned char *)passwd); + p += SCRAMBLE_LENGTH; + } else { + *p++ = '\0'; + } + + /* 3. db ASCIIZ */ + if (db[0]) { + 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)) { + DBG_RETURN(FAIL); + } + + 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; + + if (conn->error_info.error_no) { + ret = FAIL; + /* + COM_CHANGE_USER is broken in 5.1. At least in 5.1.15 and 5.1.14, 5.1.11 is immune. + bug#25371 mysql_change_user() triggers "packets out of sync" + When it gets fixed, there should be one more check here + */ + if (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); + 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) { + char * tmp = NULL; + /* if we get conn->user as parameter and then we first free it, then estrndup it, we will crash */ + tmp = mnd_pestrndup(user, user_len, conn->persistent); + if (conn->user) { + mnd_pefree(conn->user, conn->persistent); + } + conn->user = tmp; + + tmp = mnd_pestrdup(passwd, conn->persistent); + if (conn->passwd) { + mnd_pefree(conn->passwd, conn->persistent); + } + conn->passwd = tmp; + + if (conn->last_message) { + mnd_pefree(conn->last_message, conn->persistent); + conn->last_message = NULL; + } + memset(&conn->upsert_status, 0, sizeof(conn->upsert_status)); + /* set charset for old servers */ + if (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); + } +end: + PACKET_FREE(chg_user_resp); + + /* + Here we should close all statements. Unbuffered queries should not be a + problem as we won't allow sending COM_CHANGE_USER. + */ + DBG_INF(ret == PASS? "PASS":"FAIL"); + DBG_RETURN(ret); +} +/* }}} */ + + /* {{{ mysqlnd_conn::query */ /* If conn->error_info.error_no is not zero, then we had an error. @@ -1869,149 +2007,6 @@ PHPAPI const char *mysqlnd_field_type_name(enum mysqlnd_field_types field_type) /* }}} */ -/* {{{ mysqlnd_conn::change_user */ -static enum_func_status -MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn, - const char *user, - const char *passwd, - const char *db, - zend_bool silent TSRMLS_DC) -{ - /* - 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 - buffer overflows. - */ - size_t user_len; - 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 + 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=%u", - conn->thread_id, user?user:"", passwd?"***":"null", db?db:"", (silent == TRUE)?1:0 ); - - SET_ERROR_AFF_ROWS(conn); - - if (!user) { - user = ""; - } - if (!passwd) { - passwd = ""; - } - if (!db) { - db = ""; - } - - /* 1. user ASCIIZ */ - user_len = MIN(strlen(user), MYSQLND_MAX_ALLOWED_USER_LEN); - memcpy(p, user, user_len); - p += user_len; - *p++ = '\0'; - - /* 2. password SCRAMBLE_LENGTH followed by the scramble or \0 */ - if (passwd[0]) { - *p++ = SCRAMBLE_LENGTH; - php_mysqlnd_scramble((unsigned char *)p, conn->scramble, (unsigned char *)passwd); - p += SCRAMBLE_LENGTH; - } else { - *p++ = '\0'; - } - - /* 3. db ASCIIZ */ - if (db[0]) { - 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)) { - DBG_RETURN(FAIL); - } - - 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; - - if (conn->error_info.error_no) { - ret = FAIL; - /* - COM_CHANGE_USER is broken in 5.1. At least in 5.1.15 and 5.1.14, 5.1.11 is immune. - bug#25371 mysql_change_user() triggers "packets out of sync" - When it gets fixed, there should be one more check here - */ - if (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); - 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) { - char * tmp = NULL; - /* if we get conn->user as parameter and then we first free it, then estrndup it, we will crash */ - tmp = mnd_pestrndup(user, user_len, conn->persistent); - if (conn->user) { - mnd_pefree(conn->user, conn->persistent); - } - conn->user = tmp; - - tmp = mnd_pestrdup(passwd, conn->persistent); - if (conn->passwd) { - mnd_pefree(conn->passwd, conn->persistent); - } - conn->passwd = tmp; - - if (conn->last_message) { - mnd_pefree(conn->last_message, conn->persistent); - conn->last_message = NULL; - } - memset(&conn->upsert_status, 0, sizeof(conn->upsert_status)); - /* set charset for old servers */ - if (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); - } -end: - PACKET_FREE(chg_user_resp); - - /* - Here we should close all statements. Unbuffered queries should not be a - problem as we won't allow sending COM_CHANGE_USER. - */ - DBG_INF(ret == PASS? "PASS":"FAIL"); - DBG_RETURN(ret); -} -/* }}} */ - - /* {{{ mysqlnd_conn::set_client_option */ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn,