diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 14:11:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 14:11:00 +0000 |
commit | af754e596a8dbb05ed8580c342e7fe02e08b28e0 (patch) | |
tree | b2f334c2b55ede42081aa6710a72da784547d8ea /src/modules/rlm_sql/drivers/rlm_sql_unixodbc/rlm_sql_unixodbc.c | |
parent | Initial commit. (diff) | |
download | freeradius-af754e596a8dbb05ed8580c342e7fe02e08b28e0.tar.xz freeradius-af754e596a8dbb05ed8580c342e7fe02e08b28e0.zip |
Adding upstream version 3.2.3+dfsg.upstream/3.2.3+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/modules/rlm_sql/drivers/rlm_sql_unixodbc/rlm_sql_unixodbc.c')
-rw-r--r-- | src/modules/rlm_sql/drivers/rlm_sql_unixodbc/rlm_sql_unixodbc.c | 364 |
1 files changed, 364 insertions, 0 deletions
diff --git a/src/modules/rlm_sql/drivers/rlm_sql_unixodbc/rlm_sql_unixodbc.c b/src/modules/rlm_sql/drivers/rlm_sql_unixodbc/rlm_sql_unixodbc.c new file mode 100644 index 0000000..248b55e --- /dev/null +++ b/src/modules/rlm_sql/drivers/rlm_sql_unixodbc/rlm_sql_unixodbc.c @@ -0,0 +1,364 @@ +/* + * sql_unixodbc.c unixODBC rlm_sql driver + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Copyright 2000,2006 The FreeRADIUS server project + * Copyright 2000 Dmitri Ageev <d_ageev@ortcc.ru> + */ + +RCSID("$Id$") + +#include <freeradius-devel/radiusd.h> +#include <freeradius-devel/rad_assert.h> + +#include <sqltypes.h> +#include "rlm_sql.h" + +typedef struct rlm_sql_unixodbc_conn { + SQLHENV env; + SQLHDBC dbc; + SQLHSTMT stmt; + rlm_sql_row_t row; + void *conn; +} rlm_sql_unixodbc_conn_t; + +USES_APPLE_DEPRECATED_API +#include <sql.h> +#include <sqlext.h> + +/* Forward declarations */ +static int sql_check_error(long err_handle, rlm_sql_handle_t *handle, rlm_sql_config_t *config); +static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, rlm_sql_config_t *config); +static int sql_affected_rows(rlm_sql_handle_t *handle, rlm_sql_config_t *config); +static int sql_num_fields(rlm_sql_handle_t *handle, rlm_sql_config_t *config); + +static int _sql_socket_destructor(rlm_sql_unixodbc_conn_t *conn) +{ + DEBUG2("rlm_sql_unixodbc: Socket destructor called, closing socket"); + + if (conn->stmt) SQLFreeStmt(conn->stmt, SQL_DROP); + + if (conn->dbc) { + SQLDisconnect(conn->dbc); + SQLFreeConnect(conn->dbc); + } + + if (conn->env) SQLFreeEnv(conn->env); + + return 0; +} + +static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config) +{ + rlm_sql_unixodbc_conn_t *conn; + long err_handle; + + MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_unixodbc_conn_t)); + talloc_set_destructor(conn, _sql_socket_destructor); + + /* 1. Allocate environment handle and register version */ + err_handle = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &conn->env); + if (sql_check_error(err_handle, handle, config)) { + ERROR("rlm_sql_unixodbc: Can't allocate environment handle"); + return RLM_SQL_ERROR; + } + + err_handle = SQLSetEnvAttr(conn->env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); + if (sql_check_error(err_handle, handle, config)) { + ERROR("rlm_sql_unixodbc: Can't register ODBC version"); + return RLM_SQL_ERROR; + } + + /* 2. Allocate connection handle */ + err_handle = SQLAllocHandle(SQL_HANDLE_DBC, conn->env, &conn->dbc); + if (sql_check_error(err_handle, handle, config)) { + ERROR("rlm_sql_unixodbc: Can't allocate connection handle"); + return RLM_SQL_ERROR; + } + + /* 3. Connect to the datasource */ + { + SQLCHAR *odbc_server, *odbc_login, *odbc_password; + + memcpy(&odbc_server, &config->sql_server, sizeof(odbc_server)); + memcpy(&odbc_login, &config->sql_login, sizeof(odbc_login)); + memcpy(&odbc_password, &config->sql_password, sizeof(odbc_password)); + err_handle = SQLConnect(conn->dbc, + odbc_server, strlen(config->sql_server), + odbc_login, strlen(config->sql_login), + odbc_password, strlen(config->sql_password)); + } + + if (sql_check_error(err_handle, handle, config)) { + ERROR("rlm_sql_unixodbc: Connection failed"); + return RLM_SQL_ERROR; + } + + /* 4. Allocate the stmt */ + err_handle = SQLAllocHandle(SQL_HANDLE_STMT, conn->dbc, &conn->stmt); + if (sql_check_error(err_handle, handle, config)) { + ERROR("rlm_sql_unixodbc: Can't allocate the stmt"); + return RLM_SQL_ERROR; + } + + return RLM_SQL_OK; +} + +static sql_rcode_t sql_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query) +{ + rlm_sql_unixodbc_conn_t *conn = handle->conn; + long err_handle; + int state; + + /* Executing query */ + { + SQLCHAR *odbc_query; + + memcpy(&odbc_query, &query, sizeof(odbc_query)); + err_handle = SQLExecDirect(conn->stmt, odbc_query, strlen(query)); + } + if ((state = sql_check_error(err_handle, handle, config))) { + if(state == RLM_SQL_RECONNECT) { + DEBUG("rlm_sql_unixodbc: rlm_sql will attempt to reconnect"); + } + return state; + } + return 0; +} + +static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query) +{ + rlm_sql_unixodbc_conn_t *conn = handle->conn; + SQLINTEGER i; + SQLLEN len; + int colcount; + int state; + + /* Only state = 0 means success */ + if ((state = sql_query(handle, config, query))) { + return state; + } + + colcount = sql_num_fields(handle, config); + if (colcount < 0) { + return RLM_SQL_ERROR; + } + + /* Reserving memory for result */ + conn->row = talloc_zero_array(conn, char *, colcount + 1); /* Space for pointers */ + + for (i = 1; i <= colcount; i++) { + len = 0; + SQLColAttributes(conn->stmt, ((SQLUSMALLINT) i), SQL_DESC_LENGTH, NULL, 0, NULL, &len); + conn->row[i - 1] = talloc_array(conn->row, char, ++len); + SQLBindCol(conn->stmt, i, SQL_C_CHAR, (SQLCHAR *)conn->row[i - 1], len, NULL); + } + + return RLM_SQL_OK; +} + +static int sql_num_fields(rlm_sql_handle_t *handle, rlm_sql_config_t *config) +{ + rlm_sql_unixodbc_conn_t *conn = handle->conn; + long err_handle; + SQLSMALLINT num_fields = 0; + + err_handle = SQLNumResultCols(conn->stmt,&num_fields); + if (sql_check_error(err_handle, handle, config)) return -1; + + return num_fields; +} + +static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, rlm_sql_config_t *config) +{ + rlm_sql_unixodbc_conn_t *conn = handle->conn; + long err_handle; + int state; + + handle->row = NULL; + + err_handle = SQLFetch(conn->stmt); + if (err_handle == SQL_NO_DATA_FOUND) return RLM_SQL_NO_MORE_ROWS; + + if ((state = sql_check_error(err_handle, handle, config))) return state; + + handle->row = conn->row; + return RLM_SQL_OK; +} + +static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t * handle, rlm_sql_config_t *config) +{ + rlm_sql_unixodbc_conn_t *conn = handle->conn; + + sql_free_result(handle, config); + + /* + * SQL_CLOSE - The cursor (if any) associated with the statement + * handle (StatementHandle) is closed and all pending results are + * discarded. The application can reopen the cursor by calling + * SQLExecute() with the same or different values in the + * application variables (if any) that are bound to StatementHandle. + * If no cursor has been associated with the statement handle, + * this option has no effect (no warning or error is generated). + * + * So, this call does NOT free the statement at all, it merely + * resets it for the next call. This is terrible terrible naming. + */ + SQLFreeStmt(conn->stmt, SQL_CLOSE); + + return 0; +} + +static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) +{ + rlm_sql_unixodbc_conn_t *conn = handle->conn; + + SQLFreeStmt(conn->stmt, SQL_CLOSE); + + return 0; +} + +static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) +{ + rlm_sql_unixodbc_conn_t *conn = handle->conn; + + TALLOC_FREE(conn->row); + + return 0; +} + +/** Retrieves any errors associated with the connection handle + * + * @note Caller will free any memory allocated in ctx. + * + * @param ctx to allocate temporary error buffers in. + * @param out Array of sql_log_entrys to fill. + * @param outlen Length of out array. + * @param handle rlm_sql connection handle. + * @param config rlm_sql config. + * @return number of errors written to the sql_log_entry array. + */ +static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen, + rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) +{ + rlm_sql_unixodbc_conn_t *conn = handle->conn; + SQLCHAR state[256]; + SQLCHAR errbuff[256]; + SQLINTEGER errnum = 0; + SQLSMALLINT length = 255; + + rad_assert(outlen > 0); + + errbuff[0] = state[0] = '\0'; + SQLError(conn->env, conn->dbc, conn->stmt, state, &errnum, + errbuff, sizeof(errbuff), &length); + if (errnum == 0) return 0; + + out[0].type = L_ERR; + out[0].msg = talloc_asprintf(ctx, "%s: %s", state, errbuff); + + return 1; +} + +/** Checks the error code to determine if the connection needs to be re-esttablished + * + * @param error_handle Return code from a failed unixodbc call. + * @param handle rlm_sql connection handle. + * @param config rlm_sql config. + * @return RLM_SQL_OK on success, RLM_SQL_RECONNECT if reconnect is needed, or RLM_SQL_ERROR on error. + */ +static sql_rcode_t sql_check_error(long error_handle, rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) +{ + SQLCHAR state[256]; + SQLCHAR error[256]; + SQLINTEGER errornum = 0; + SQLSMALLINT length = 255; + int res = -1; + + rlm_sql_unixodbc_conn_t *conn = handle->conn; + + if (SQL_SUCCEEDED(error_handle)) return 0; /* on success, just return 0 */ + + error[0] = state[0] = '\0'; + + SQLError(conn->env, conn->dbc, conn->stmt, state, &errornum, + error, sizeof(error), &length); + + if (state[0] == '0') { + switch (state[1]) { + /* SQLSTATE 01 class contains info and warning messages */ + case '1': + INFO("rlm_sql_unixodbc: %s %s", state, error); + /* FALL-THROUGH */ + case '0': /* SQLSTATE 00 class means success */ + res = RLM_SQL_OK; + break; + + /* SQLSTATE 08 class describes various connection errors */ + case '8': + ERROR("rlm_sql_unixodbc: SQL down %s %s", state, error); + res = RLM_SQL_RECONNECT; + break; + + /* any other SQLSTATE means error */ + default: + ERROR("rlm_sql_unixodbc: %s %s", state, error); + res = RLM_SQL_ERROR; + break; + } + } else { + ERROR("rlm_sql_unixodbc: %s %s", state, error); + } + + return res; +} + +/************************************************************************* + * + * Function: sql_affected_rows + * + * Purpose: Return the number of rows affected by the query (update, + * or insert) + * + *************************************************************************/ +static int sql_affected_rows(rlm_sql_handle_t *handle, rlm_sql_config_t *config) +{ + rlm_sql_unixodbc_conn_t *conn = handle->conn; + long error_handle; + SQLLEN affected_rows; + + error_handle = SQLRowCount(conn->stmt, &affected_rows); + if (sql_check_error(error_handle, handle, config)) return -1; + + return affected_rows; +} + + +/* Exported to rlm_sql */ +extern rlm_sql_module_t rlm_sql_unixodbc; +rlm_sql_module_t rlm_sql_unixodbc = { + .name = "rlm_sql_unixodbc", + .sql_socket_init = sql_socket_init, + .sql_query = sql_query, + .sql_select_query = sql_select_query, + .sql_num_fields = sql_num_fields, + .sql_affected_rows = sql_affected_rows, + .sql_fetch_row = sql_fetch_row, + .sql_free_result = sql_free_result, + .sql_error = sql_error, + .sql_finish_query = sql_finish_query, + .sql_finish_select_query = sql_finish_select_query +}; |