From caceb1dfe2adc230f013b3bfab6939912367fa5e Mon Sep 17 00:00:00 2001 From: "/C=EU/ST=EU/CN=Pablo Neira Ayuso/emailAddress=pablo@netfilter.org" Date: Tue, 19 Jun 2007 11:33:55 +0000 Subject: Marius Tomaschewski - included inttypes.h and changed to use PRId64/PRIu64 format definitions to use correct number of l's on all platforms (WORDSIZE depending). - added str flag to the struct _field - it is set in the mysql_get_columns function when the IP-Addr columns in the database are non-numeric/string and used to insert proper converted value. See also the --with-mysql-log-ip-as-string configure option and doc dir. - changed to use snprintf instead of sprintf to take care about the allocated buffer length: STMT_ADD macro - added global stmt_siz variable for the allocated size of the stmt buffer (set in mysql_createstmt, used in mysql_output; obsoletes local size variable in mysql_createstmt). - Added guard at the beginning of mysql_output() triggering reconnect if stmt_val == NULL. - Fixed to check in mysql_output() if the buffer contains enough place for mysql_*escape_string calls -- see mysql documentation. - Fixed to close the old db handle before reconnect is initiated on failure of mysql_real_query() in mysql_output. - Increased size of allocated stmt buffer in mysql_createstmt() to +1. - Fixed strncpy calls in mysql_createstmt and mysql_get_columns; use ULOGD_MAX_KEYLEN-1, not ULOGD_MAX_KEYLEN and terminate the buffer explicitelly after (in case the source string was longer / not term.). - Fixed mysql_open_db() to close db handle after connect failure. - Fixed _mysql_init_db() to close db handle after mysql_get_columns failure. - Fixed _mysql_fini to call mysql_close(dbh) only if dbh not NULL and reset it to NULL after. --- ulogd/mysql/ulogd_MYSQL.c | 133 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 94 insertions(+), 39 deletions(-) (limited to 'ulogd') diff --git a/ulogd/mysql/ulogd_MYSQL.c b/ulogd/mysql/ulogd_MYSQL.c index 9014a29..027781b 100644 --- a/ulogd/mysql/ulogd_MYSQL.c +++ b/ulogd/mysql/ulogd_MYSQL.c @@ -43,6 +43,7 @@ #include #include #include +#include #ifdef DEBUG_MYSQL #define DEBUGP(x, args...) fprintf(stderr, x, ## args) @@ -53,6 +54,7 @@ struct _field { char name[ULOGD_MAX_KEYLEN]; unsigned int id; + unsigned int str; struct _field *next; }; @@ -68,12 +70,21 @@ static struct _field *fields; /* buffer for our insert statement */ static char *stmt; +/* size of our insert statement buffer */ +static size_t stmt_siz; + /* pointer to the beginning of the "VALUES" part */ static char *stmt_val; /* pointer to current inser position in statement */ static char *stmt_ins; +#define STMT_ADD(fmt...) \ + do { \ + if (stmt_ins >= stmt && stmt_siz > stmt_ins - stmt) \ + snprintf(stmt_ins, stmt_siz-(stmt_ins-stmt), ##fmt); \ + } while(0) + /* Attempt to reconnect if connection is lost */ time_t reconnect = 0; #define TIME_ERR ((time_t)-1) /* Be paranoid */ @@ -132,6 +143,7 @@ static config_entry_t connect_timeout_ce = { }; static int _mysql_init_db(ulog_iret_t *result); +static void _mysql_fini(void); /* our main output function, called by ulogd */ static int mysql_output(ulog_iret_t *result) @@ -142,6 +154,12 @@ static int mysql_output(ulog_iret_t *result) char *tmpstr; /* need this for --log-ip-as-string */ struct in_addr addr; #endif + size_t esclen; + + if (stmt_val == NULL) { + _mysql_fini(); + return _mysql_init_db(result); + } stmt_ins = stmt_val; @@ -155,60 +173,74 @@ static int mysql_output(ulog_iret_t *result) if (!res || !IS_VALID((*res))) { /* no result, we have to fake something */ - sprintf(stmt_ins, "NULL,"); + STMT_ADD("NULL,"); stmt_ins = stmt + strlen(stmt); continue; } switch (res->type) { case ULOGD_RET_INT8: - sprintf(stmt_ins, "%d,", res->value.i8); + STMT_ADD("%d,", res->value.i8); break; case ULOGD_RET_INT16: - sprintf(stmt_ins, "%d,", res->value.i16); + STMT_ADD("%d,", res->value.i16); break; case ULOGD_RET_INT32: - sprintf(stmt_ins, "%d,", res->value.i32); + STMT_ADD("%d,", res->value.i32); break; case ULOGD_RET_INT64: - sprintf(stmt_ins, "%lld,", res->value.i64); + STMT_ADD("%"PRId64",", res->value.i64); break; case ULOGD_RET_UINT8: - sprintf(stmt_ins, "%u,", res->value.ui8); + STMT_ADD("%u,", res->value.ui8); break; case ULOGD_RET_UINT16: - sprintf(stmt_ins, "%u,", res->value.ui16); + STMT_ADD("%u,", res->value.ui16); break; case ULOGD_RET_IPADDR: #ifdef IP_AS_STRING - memset(&addr, 0, sizeof(addr)); - addr.s_addr = ntohl(res->value.ui32); - *stmt_ins++ = '\''; - tmpstr = inet_ntoa(addr); + if (f->str) { + addr.s_addr = ntohl(res->value.ui32); + tmpstr = inet_ntoa(addr); + esclen = (strlen(tmpstr)*2) + 4; + if (stmt_siz <= (stmt_ins-stmt)+esclen){ + STMT_ADD("'',"); + break; + } + + *stmt_ins++ = '\''; #ifdef OLD_MYSQL - mysql_escape_string(stmt_ins, tmpstr, - strlen(tmpstr)); + mysql_escape_string(stmt_ins, + tmpstr, + strlen(tmpstr)); #else - mysql_real_escape_string(dbh, stmt_ins, - tmpstr, - strlen(tmpstr)); + mysql_real_escape_string(dbh, + stmt_ins, + tmpstr, + strlen(tmpstr)); #endif /* OLD_MYSQL */ - stmt_ins = stmt + strlen(stmt); - sprintf(stmt_ins, "',"); - break; + stmt_ins = stmt + strlen(stmt); + STMT_ADD("',"); + break; + } #endif /* IP_AS_STRING */ /* EVIL: fallthrough when logging IP as * u_int32_t */ case ULOGD_RET_UINT32: - sprintf(stmt_ins, "%u,", res->value.ui32); + STMT_ADD("%u,", res->value.ui32); break; case ULOGD_RET_UINT64: - sprintf(stmt_ins, "%llu,", res->value.ui64); + STMT_ADD("%"PRIu64",", res->value.ui64); break; case ULOGD_RET_BOOL: - sprintf(stmt_ins, "'%d',", res->value.b); + STMT_ADD("'%d',", res->value.b); break; case ULOGD_RET_STRING: + esclen = (strlen(res->value.ptr)*2) + 4; + if (stmt_siz <= (stmt_ins-stmt) + esclen) { + STMT_ADD("'',"); + break; + } *stmt_ins++ = '\''; #ifdef OLD_MYSQL mysql_escape_string(stmt_ins, res->value.ptr, @@ -218,8 +250,7 @@ static int mysql_output(ulog_iret_t *result) res->value.ptr, strlen(res->value.ptr)); #endif stmt_ins = stmt + strlen(stmt); - sprintf(stmt_ins, "',"); - /* sprintf(stmt_ins, "'%s',", res->value.ptr); */ + STMT_ADD("',"); break; case ULOGD_RET_RAW: ulogd_log(ULOGD_NOTICE, @@ -235,6 +266,8 @@ static int mysql_output(ulog_iret_t *result) stmt_ins = stmt + strlen(stmt); } *(stmt_ins - 1) = ')'; + *stmt_ins = '\0'; + DEBUGP("stmt=#%s#\n", stmt); /* now we have created our statement, insert it */ @@ -242,6 +275,7 @@ static int mysql_output(ulog_iret_t *result) if (mysql_real_query(dbh, stmt, strlen(stmt))) { ulogd_log(ULOGD_ERROR, "sql error during insert: %s\n", mysql_error(dbh)); + _mysql_fini(); return _mysql_init_db(result); } @@ -261,7 +295,6 @@ static int mysql_output_disabled(ulog_iret_t *result) static int mysql_createstmt(void) { struct _field *f; - unsigned int size; char buf[ULOGD_MAX_KEYLEN]; char *underscore; @@ -269,36 +302,40 @@ static int mysql_createstmt(void) free(stmt); /* caclulate the size for the insert statement */ - size = strlen(MYSQL_INSERTTEMPL) + strlen(table_ce.u.string); + stmt_siz = strlen(MYSQL_INSERTTEMPL) + strlen(table_ce.u.string) + 1; for (f = fields; f; f = f->next) { /* we need space for the key and a comma, as well as * enough space for the values */ - size += strlen(f->name) + 1 + MYSQL_VALSIZE; + stmt_siz += strlen(f->name) + 1 + MYSQL_VALSIZE; } - ulogd_log(ULOGD_DEBUG, "allocating %u bytes for statement\n", size); + ulogd_log(ULOGD_DEBUG, "allocating %zu bytes for statement\n", + stmt_siz); - stmt = (char *) malloc(size); + stmt = (char *) malloc(stmt_siz); if (!stmt) { + stmt_val = NULL; + stmt_siz = 0; ulogd_log(ULOGD_ERROR, "OOM!\n"); return -1; } - sprintf(stmt, "insert into %s (", table_ce.u.string); + snprintf(stmt, stmt_siz, "insert into %s (", table_ce.u.string); stmt_val = stmt + strlen(stmt); for (f = fields; f; f = f->next) { - strncpy(buf, f->name, ULOGD_MAX_KEYLEN); + strncpy(buf, f->name, ULOGD_MAX_KEYLEN-1); + buf[ULOGD_MAX_KEYLEN-1] = '\0'; while ((underscore = strchr(buf, '.'))) *underscore = '_'; - sprintf(stmt_val, "%s,", buf); + STMT_ADD(stmt_val,stmt,stmt_siz, "%s,", buf); stmt_val = stmt + strlen(stmt); } *(stmt_val - 1) = ')'; - sprintf(stmt_val, " values ("); + STMT_ADD(stmt_val,stmt,stmt_siz, " values ("); stmt_val = stmt + strlen(stmt); ulogd_log(ULOGD_DEBUG, "stmt='%s'\n", stmt); @@ -333,7 +370,9 @@ static int mysql_get_columns(const char *table) while ((field = mysql_fetch_field(result))) { /* replace all underscores with dots */ - strncpy(buf, field->name, ULOGD_MAX_KEYLEN); + strncpy(buf, field->name, ULOGD_MAX_KEYLEN-1); + buf[ULOGD_MAX_KEYLEN-1] = '\0'; + while ((underscore = strchr(buf, '_'))) *underscore = '.'; @@ -352,8 +391,10 @@ static int mysql_get_columns(const char *table) ulogd_log(ULOGD_ERROR, "OOM!\n"); return -1; } - strncpy(f->name, buf, ULOGD_MAX_KEYLEN); + strncpy(f->name, buf, ULOGD_MAX_KEYLEN-1); + f->name[ULOGD_MAX_KEYLEN-1] = '\0'; f->id = id; + f->str = !IS_NUM(field->type); f->next = fields; fields = f; } @@ -371,10 +412,14 @@ static int mysql_open_db(char *server, int port, char *user, char *pass, return -1; if (connect_timeout_ce.u.value) - mysql_options(dbh, MYSQL_OPT_CONNECT_TIMEOUT, (const char *) &connect_timeout_ce.u.value); + mysql_options(dbh, MYSQL_OPT_CONNECT_TIMEOUT, + (const char *) &connect_timeout_ce.u.value); if (!mysql_real_connect(dbh, server, user, pass, db, port, NULL, 0)) + { + _mysql_fini(); return -1; + } return 0; } @@ -413,10 +458,17 @@ static int _mysql_init_db(ulog_iret_t *result) /* read the fieldnames to know which values to insert */ if (mysql_get_columns(table_ce.u.string)) { ulogd_log(ULOGD_ERROR, "unable to get mysql columns\n"); + _mysql_fini(); return init_reconnect(); } - mysql_createstmt(); - + + if (mysql_createstmt()) + { + ulogd_log(ULOGD_ERROR, "unable to create mysql statement\n"); + _mysql_fini(); + return init_reconnect(); + } + /* enable plugin */ mysql_plugin.output = &mysql_output; @@ -438,7 +490,10 @@ static int _mysql_init(void) static void _mysql_fini(void) { - mysql_close(dbh); + if (dbh) { + mysql_close(dbh); + dbh = NULL; + } } static ulog_output_t mysql_plugin = { -- cgit v1.2.3