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_iodbc/rlm_sql_iodbc.c | |
parent | Initial commit. (diff) | |
download | freeradius-b95e0cd7685e6bbc2465b1f4efe1884df2f4ef01.tar.xz freeradius-b95e0cd7685e6bbc2465b1f4efe1884df2f4ef01.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_iodbc/rlm_sql_iodbc.c')
-rw-r--r-- | src/modules/rlm_sql/drivers/rlm_sql_iodbc/rlm_sql_iodbc.c | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/src/modules/rlm_sql/drivers/rlm_sql_iodbc/rlm_sql_iodbc.c b/src/modules/rlm_sql/drivers/rlm_sql_iodbc/rlm_sql_iodbc.c new file mode 100644 index 0000000..d87444d --- /dev/null +++ b/src/modules/rlm_sql/drivers/rlm_sql_iodbc/rlm_sql_iodbc.c @@ -0,0 +1,295 @@ +/* + * sql_iodbc.c iODBC support for FreeRadius + * + * Version: $Id$ + * + * 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 Jeff Carneal <jeff@apex.net> + */ + +RCSID("$Id$") +USES_APPLE_DEPRECATED_API + +#include <freeradius-devel/radiusd.h> +#include <freeradius-devel/rad_assert.h> + +#include <sys/stat.h> + +#include <isql.h> +#include <isqlext.h> +#include <sqltypes.h> + +#include "rlm_sql.h" + +#define IODBC_MAX_ERROR_LEN 256 + +typedef struct rlm_sql_iodbc_conn { + HENV env_handle; + HDBC dbc_handle; + HSTMT stmt; + int id; + + rlm_sql_row_t row; + + struct sql_socket *next; + + void *conn; +} rlm_sql_iodbc_conn_t; + +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); +static int sql_num_fields(rlm_sql_handle_t *handle, rlm_sql_config_t *config); + +static int _sql_socket_destructor(rlm_sql_iodbc_conn_t *conn) +{ + DEBUG2("rlm_sql_iodbc: Socket destructor called, closing socket"); + + if (conn->stmt) SQLFreeStmt(conn->stmt, SQL_DROP); + + if (conn->dbc_handle) { + SQLDisconnect(conn->dbc_handle); + SQLFreeConnect(conn->dbc_handle); + } + + if (conn->env_handle) SQLFreeEnv(conn->env_handle); + + return 0; +} + +static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config) +{ + + rlm_sql_iodbc_conn_t *conn; + SQLRETURN rcode; + sql_log_entry_t entry; + + MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_iodbc_conn_t)); + talloc_set_destructor(conn, _sql_socket_destructor); + + rcode = SQLAllocEnv(&conn->env_handle); + if (!SQL_SUCCEEDED(rcode)) { + ERROR("rlm_sql_iodbc: SQLAllocEnv failed"); + if (sql_error(NULL, &entry, 1, handle, config) > 0) ERROR("rlm_sql_iodbc: %s", entry.msg); + + return RLM_SQL_ERROR; + } + + rcode = SQLAllocConnect(conn->env_handle, + &conn->dbc_handle); + if (!SQL_SUCCEEDED(rcode)) { + ERROR("rlm_sql_iodbc: SQLAllocConnect failed"); + if (sql_error(NULL, &entry, 1, handle, config) > 0) ERROR("rlm_sql_iodbc: %s", entry.msg); + + return RLM_SQL_ERROR; + } + + /* + * The iodbc API doesn't qualify arguments as const even when they should be. + */ + { + SQLCHAR *server, *login, *password; + + memcpy(&server, &config->sql_server, sizeof(server)); + memcpy(&login, &config->sql_login, sizeof(login)); + memcpy(&password, &config->sql_password, sizeof(password)); + + rcode = SQLConnect(conn->dbc_handle, server, SQL_NTS, login, SQL_NTS, password, SQL_NTS); + } + if (!SQL_SUCCEEDED(rcode)) { + ERROR("rlm_sql_iodbc: SQLConnectfailed"); + if (sql_error(NULL, &entry, 1, handle, config) > 0) ERROR("rlm_sql_iodbc: %s", entry.msg); + + return RLM_SQL_ERROR; + } + + return 0; +} + +static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config, char const *query) +{ + rlm_sql_iodbc_conn_t *conn = handle->conn; + SQLRETURN rcode; + + rcode = SQLAllocStmt(conn->dbc_handle, &conn->stmt); + if (!SQL_SUCCEEDED(rcode)) return RLM_SQL_ERROR; + + if (!conn->dbc_handle) { + ERROR("rlm_sql_iodbc: Socket not connected"); + return RLM_SQL_ERROR; + } + + { + SQLCHAR *statement; + + memcpy(&statement, &query, sizeof(statement)); + rcode = SQLExecDirect(conn->stmt, statement, SQL_NTS); + } + + if (!SQL_SUCCEEDED(rcode)) return RLM_SQL_ERROR; + + return 0; +} + +static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query) +{ + int numfields = 0; + int i = 0; + char **row = NULL; + long len = 0; + rlm_sql_iodbc_conn_t *conn = handle->conn; + + if (sql_query(handle, config, query) < 0) return RLM_SQL_ERROR; + + numfields = sql_num_fields(handle, config); + + row = (char **) rad_malloc(sizeof(char *) * (numfields+1)); + memset(row, 0, (sizeof(char *) * (numfields))); + row[numfields] = NULL; + + for(i=1; i<=numfields; i++) { + SQLColAttributes(conn->stmt, ((SQLUSMALLINT) i), SQL_COLUMN_LENGTH, NULL, 0, NULL, &len); + len++; + + /* + * Allocate space for each column + */ + row[i - 1] = rad_malloc((size_t) len); + + /* + * This makes me feel dirty, but, according to Microsoft, it works. + * Any ODBC datatype can be converted to a 'char *' according to + * the following: + * + * http://msdn.microsoft.com/library/psdk/dasdk/odap4o4z.htm + */ + SQLBindCol(conn->stmt, i, SQL_C_CHAR, (SQLCHAR *)row[i-1], len, 0); + } + + conn->row = row; + + return 0; +} + +static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) +{ + + SQLSMALLINT count=0; + rlm_sql_iodbc_conn_t *conn = handle->conn; + + SQLNumResultCols(conn->stmt, &count); + + return (int)count; +} + +static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) +{ + SQLRETURN rc; + rlm_sql_iodbc_conn_t *conn = handle->conn; + + handle->row = NULL; + + rc = SQLFetch(conn->stmt); + if (rc == SQL_NO_DATA_FOUND) return RLM_SQL_NO_MORE_ROWS; + + /* XXX Check rc for database down, if so, return RLM_SQL_RECONNECT */ + + handle->row = conn->row; + return 0; +} + +static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, rlm_sql_config_t *config) +{ + int i = 0; + rlm_sql_iodbc_conn_t *conn = handle->conn; + + for (i = 0; i < sql_num_fields(handle, config); i++) free(conn->row[i]); + free(conn->row); + conn->row = NULL; + + SQLFreeStmt(conn->stmt, SQL_DROP); + conn->stmt = NULL; + + 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_iodbc_conn_t *conn = handle->conn; + SQLINTEGER errornum = 0; + SQLSMALLINT length = 0; + SQLCHAR state[256] = ""; + SQLCHAR errbuff[IODBC_MAX_ERROR_LEN]; + + rad_assert(outlen > 0); + + errbuff[0] = '\0'; + SQLError(conn->env_handle, conn->dbc_handle, conn->stmt, + state, &errornum, errbuff, IODBC_MAX_ERROR_LEN, &length); + if (errbuff[0] == '\0') return 0; + + out[0].type = L_ERR; + out[0].msg = talloc_asprintf(ctx, "%s: %s", state, errbuff); + + return 1; +} + +static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config) +{ + return sql_free_result(handle, config); +} + +static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config) +{ + return sql_free_result(handle, config); +} + +static int sql_affected_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) +{ + long count; + rlm_sql_iodbc_conn_t *conn = handle->conn; + + SQLRowCount(conn->stmt, &count); + return (int)count; +} + +/* Exported to rlm_sql */ +extern rlm_sql_module_t rlm_sql_iodbc; +rlm_sql_module_t rlm_sql_iodbc = { + .name = "rlm_sql_iodbc", + .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 +}; |