From dcfae78574b37f64288a0b525f9306f27e3087ba Mon Sep 17 00:00:00 2001 From: Joshua Colp Date: Wed, 28 May 2014 11:37:50 +0000 Subject: [PATCH] res_config_odbc: Use dynamically sized buffers to store row data so values do not get truncated. ASTERISK-23582 #close ASTERISk-23582 #comment Reported by: Walter Doekes Review: https://reviewboard.asterisk.org/r/3557/ ........ Merged revisions 414693 from http://svn.asterisk.org/svn/asterisk/branches/1.8 ........ Merged revisions 414694 from http://svn.asterisk.org/svn/asterisk/branches/11 ........ Merged revisions 414695 from http://svn.asterisk.org/svn/asterisk/branches/12 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@414696 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- funcs/func_odbc.c | 81 +++++++++++------------- res/res_config_odbc.c | 142 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 157 insertions(+), 66 deletions(-) diff --git a/funcs/func_odbc.c b/funcs/func_odbc.c index 5e7655f908..1c3a17402c 100644 --- a/funcs/func_odbc.c +++ b/funcs/func_odbc.c @@ -109,9 +109,9 @@ struct acf_odbc_query { AST_RWLIST_ENTRY(acf_odbc_query) list; char readhandle[5][30]; char writehandle[5][30]; - char sql_read[2048]; - char sql_write[2048]; - char sql_insert[2048]; + char *sql_read; + char *sql_write; + char *sql_insert; unsigned int flags; int rowlimit; struct ast_custom_function *acf; @@ -855,6 +855,23 @@ static int exec_odbcfinish(struct ast_channel *chan, const char *data) return 0; } +static int free_acf_query(struct acf_odbc_query *query) +{ + if (query) { + if (query->acf) { + if (query->acf->name) + ast_free((char *)query->acf->name); + ast_string_field_free_memory(query->acf); + ast_free(query->acf); + } + ast_free(query->sql_read); + ast_free(query->sql_write); + ast_free(query->sql_insert); + ast_free(query); + } + return 0; +} + static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query) { const char *tmp; @@ -899,35 +916,35 @@ static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_qu } if ((tmp = ast_variable_retrieve(cfg, catg, "readsql"))) - ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read)); + (*query)->sql_read = ast_strdup(tmp); else if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) { ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s. Please use 'readsql' instead.\n", catg); - ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read)); + (*query)->sql_read = ast_strdup(tmp); } if (!ast_strlen_zero((*query)->sql_read) && ast_strlen_zero((*query)->readhandle[0])) { - ast_free(*query); + free_acf_query(*query); *query = NULL; ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg); return EINVAL; } if ((tmp = ast_variable_retrieve(cfg, catg, "writesql"))) - ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write)); + (*query)->sql_write = ast_strdup(tmp); else if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) { ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s. Please use 'writesql' instead.\n", catg); - ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write)); + (*query)->sql_write = ast_strdup(tmp); } if (!ast_strlen_zero((*query)->sql_write) && ast_strlen_zero((*query)->writehandle[0])) { - ast_free(*query); + free_acf_query(*query); *query = NULL; ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg); return EINVAL; } if ((tmp = ast_variable_retrieve(cfg, catg, "insertsql"))) { - ast_copy_string((*query)->sql_insert, tmp, sizeof((*query)->sql_insert)); + (*query)->sql_insert = ast_strdup(tmp); } /* Allow escaping of embedded commas in fields to be turned off */ @@ -946,13 +963,12 @@ static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_qu (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function)); if (! (*query)->acf) { - ast_free(*query); + free_acf_query(*query); *query = NULL; return ENOMEM; } if (ast_string_field_init((*query)->acf, 128)) { - ast_free((*query)->acf); - ast_free(*query); + free_acf_query(*query); *query = NULL; return ENOMEM; } @@ -968,9 +984,7 @@ static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_qu } if (!((*query)->acf->name)) { - ast_string_field_free_memory((*query)->acf); - ast_free((*query)->acf); - ast_free(*query); + free_acf_query(*query); *query = NULL; return ENOMEM; } @@ -982,10 +996,7 @@ static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_qu } if (ast_strlen_zero((*query)->acf->syntax)) { - ast_free((char *)(*query)->acf->name); - ast_string_field_free_memory((*query)->acf); - ast_free((*query)->acf); - ast_free(*query); + free_acf_query(*query); *query = NULL; return ENOMEM; } @@ -997,10 +1008,7 @@ static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_qu } if (ast_strlen_zero((*query)->acf->synopsis)) { - ast_free((char *)(*query)->acf->name); - ast_string_field_free_memory((*query)->acf); - ast_free((*query)->acf); - ast_free(*query); + free_acf_query(*query); *query = NULL; return ENOMEM; } @@ -1042,19 +1050,14 @@ static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_qu ast_strlen_zero((*query)->sql_insert) ? "" : (*query)->sql_insert, ast_strlen_zero((*query)->sql_insert) ? "" : "\n"); } else { - ast_string_field_free_memory((*query)->acf); - ast_free((char *)(*query)->acf->name); - ast_free((*query)->acf); - ast_free(*query); + free_acf_query(*query); + *query = NULL; ast_log(LOG_WARNING, "Section '%s' was found, but there was no SQL to execute. Ignoring.\n", catg); return EINVAL; } if (ast_strlen_zero((*query)->acf->desc)) { - ast_string_field_free_memory((*query)->acf); - ast_free((char *)(*query)->acf->name); - ast_free((*query)->acf); - ast_free(*query); + free_acf_query(*query); *query = NULL; return ENOMEM; } @@ -1074,20 +1077,6 @@ static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_qu return 0; } -static int free_acf_query(struct acf_odbc_query *query) -{ - if (query) { - if (query->acf) { - if (query->acf->name) - ast_free((char *)query->acf->name); - ast_string_field_free_memory(query->acf); - ast_free(query->acf); - } - ast_free(query); - } - return 0; -} - static char *cli_odbc_read(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { AST_DECLARE_APP_ARGS(args, diff --git a/res/res_config_odbc.c b/res/res_config_odbc.c index 6fc9ea28b3..4806bd208a 100644 --- a/res/res_config_odbc.c +++ b/res/res_config_odbc.c @@ -48,6 +48,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/stringfields.h" AST_THREADSTORAGE(sql_buf); +AST_THREADSTORAGE(rowdata_buf); struct custom_prepare_struct { const char *sql; @@ -166,7 +167,7 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl SQLHSTMT stmt; char sql[1024]; char coltitle[256]; - char rowdata[2048]; + struct ast_str *rowdata = ast_str_thread_get(&rowdata_buf, 128); char *op; const struct ast_variable *field = fields; char *stringp; @@ -237,7 +238,6 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl return NULL; } for (x = 0; x < colcount; x++) { - rowdata[0] = '\0'; colsize = 0; collen = sizeof(coltitle); res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, @@ -250,14 +250,25 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl return NULL; } + ast_str_reset(rowdata); indicator = 0; - res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator); - if (indicator == SQL_NULL_DATA) - rowdata[0] = '\0'; - else if (ast_strlen_zero(rowdata)) { + + res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata), ast_str_size(rowdata), &indicator); + ast_str_update(rowdata); + if (indicator == SQL_NULL_DATA) { + ast_str_reset(rowdata); + } else if (!ast_str_strlen(rowdata)) { /* Because we encode the empty string for a NULL, we will encode * actual empty strings as a string containing a single whitespace. */ - ast_copy_string(rowdata, " ", sizeof(rowdata)); + ast_str_set(&rowdata, -1, "%s", " "); + } else if ((res == SQL_SUCCESS) || (res == SQL_SUCCESS_WITH_INFO)) { + if (indicator != ast_str_strlen(rowdata)) { + /* If the available space was not enough to contain the row data enlarge and read in the rest */ + ast_str_make_space(&rowdata, indicator + 1); + res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata) + ast_str_strlen(rowdata), + ast_str_size(rowdata) - ast_str_strlen(rowdata), &indicator); + ast_str_update(rowdata); + } } if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { @@ -267,7 +278,8 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl ast_odbc_release_obj(obj); return NULL; } - stringp = rowdata; + + stringp = ast_str_buffer(rowdata); while (stringp) { chunk = strsep(&stringp, ";"); if (!ast_strlen_zero(ast_strip(chunk))) { @@ -311,7 +323,7 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char * SQLHSTMT stmt; char sql[1024]; char coltitle[256]; - char rowdata[2048]; + struct ast_str *rowdata = ast_str_thread_get(&rowdata_buf, 128); const char *initfield; char *op; const struct ast_variable *field = fields; @@ -397,7 +409,6 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char * continue; } for (x=0;xcon, &sth); + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_verb(4, "Failure in AllocStatement %d\n", res); + return NULL; + } + + res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS); + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_verb(4, "Error in PREPARE %d\n", res); + SQLFreeHandle(SQL_HANDLE_STMT, sth); + return NULL; + } + + SQLBindCol(sth, 1, SQL_C_ULONG, &q->var_val_size, sizeof(q->var_val_size), &q->err); + + return sth; +} + static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data) { struct config_odbc_obj *q = data; @@ -831,7 +881,7 @@ static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data) SQLBindCol(sth, 1, SQL_C_ULONG, &q->cat_metric, sizeof(q->cat_metric), &q->err); SQLBindCol(sth, 2, SQL_C_CHAR, q->category, sizeof(q->category), &q->err); SQLBindCol(sth, 3, SQL_C_CHAR, q->var_name, sizeof(q->var_name), &q->err); - SQLBindCol(sth, 4, SQL_C_CHAR, q->var_val, sizeof(q->var_val), &q->err); + SQLBindCol(sth, 4, SQL_C_CHAR, q->var_val, q->var_val_size, &q->err); return sth; } @@ -862,12 +912,11 @@ static struct ast_config *config_odbc(const char *database, const char *table, c if (!obj) return NULL; - ast_build_string(&sql, &sqlleft, "SELECT cat_metric, category, var_name, var_val FROM %s ", table); - ast_build_string(&sql, &sqlleft, "WHERE filename='%s' AND commented=0 ", file); - ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name "); q.sql = sqlbuf; - stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q); + ast_build_string(&sql, &sqlleft, "SELECT MAX(LENGTH(var_val)) FROM %s WHERE filename='%s'", table, file); + + stmt = ast_odbc_prepare_and_execute(obj, length_determination_odbc_prepare, &q); if (!stmt) { ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql); @@ -890,6 +939,57 @@ static struct ast_config *config_odbc(const char *database, const char *table, c return cfg; } + /* There will be only one result for this, the maximum length of a variable value */ + if (SQLFetch(stmt) == SQL_NO_DATA) { + ast_log(LOG_NOTICE, "Failed to determine maximum length of a configuration value\n"); + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + ast_odbc_release_obj(obj); + return NULL; + } + + /* Reset stuff to a fresh state for the actual query which will retrieve all configuration */ + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + sql = sqlbuf; + sqlleft = sizeof(sqlbuf); + + ast_build_string(&sql, &sqlleft, "SELECT cat_metric, category, var_name, var_val FROM %s ", table); + ast_build_string(&sql, &sqlleft, "WHERE filename='%s' AND commented=0 ", file); + ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name "); + + q.var_val_size += 1; + q.var_val = ast_malloc(q.var_val_size); + if (!q.var_val) { + ast_log(LOG_WARNING, "Could not create buffer for reading in configuration values for '%s'\n", file); + ast_odbc_release_obj(obj); + return NULL; + } + + stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q); + + if (!stmt) { + ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql); + ast_odbc_release_obj(obj); + ast_free(q.var_val); + return NULL; + } + + res = SQLNumResultCols(stmt, &rowcount); + + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql); + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + ast_odbc_release_obj(obj); + ast_free(q.var_val); + return NULL; + } + + if (!rowcount) { + ast_log(LOG_NOTICE, "found nothing\n"); + ast_odbc_release_obj(obj); + ast_free(q.var_val); + return cfg; + } + cur_cat = ast_config_get_current_category(cfg); while ((res = SQLFetch(stmt)) != SQL_NO_DATA) { @@ -897,6 +997,7 @@ static struct ast_config *config_odbc(const char *database, const char *table, c if (!ast_config_internal_load(q.var_val, cfg, loader_flags, "", who_asked)) { SQLFreeHandle(SQL_HANDLE_STMT, stmt); ast_odbc_release_obj(obj); + ast_free(q.var_val); return NULL; } continue; @@ -918,6 +1019,7 @@ static struct ast_config *config_odbc(const char *database, const char *table, c SQLFreeHandle(SQL_HANDLE_STMT, stmt); ast_odbc_release_obj(obj); + ast_free(q.var_val); return cfg; }