From af754e596a8dbb05ed8580c342e7fe02e08b28e0 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 13 Apr 2024 16:11:00 +0200 Subject: Adding upstream version 3.2.3+dfsg. Signed-off-by: Daniel Baumann --- .../drivers/rlm_sql_freetds/rlm_sql_freetds.c | 773 +++++++++++++++++++++ 1 file changed, 773 insertions(+) create mode 100644 src/modules/rlm_sql/drivers/rlm_sql_freetds/rlm_sql_freetds.c (limited to 'src/modules/rlm_sql/drivers/rlm_sql_freetds/rlm_sql_freetds.c') diff --git a/src/modules/rlm_sql/drivers/rlm_sql_freetds/rlm_sql_freetds.c b/src/modules/rlm_sql/drivers/rlm_sql_freetds/rlm_sql_freetds.c new file mode 100644 index 0000000..a5c3b93 --- /dev/null +++ b/src/modules/rlm_sql/drivers/rlm_sql_freetds/rlm_sql_freetds.c @@ -0,0 +1,773 @@ + /* + * This program is 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 + */ + +/** + * $Id$ + * @file rlm_sql.c + * @brief Implements FreeTDS rlm_sql driver. + * + * @copyright 2013 Arran Cudbard-Bell + * @copyright 2000,2006 The FreeRADIUS server project + * @copyright 2000 Mattias Sjostrom + */ + +RCSID("$Id$") + +#include +#include + +#include + +#include + +#include "rlm_sql.h" + +typedef struct rlm_sql_freetds_conn { + CS_CONTEXT *context; //!< Structure FreeTDS uses to avoid creating globals. + CS_CONNECTION *db; //!< Handle specifying a single connection to the database. + CS_COMMAND *command; //!< A prepared statement. + char **results; //!< Result strings from statement execution. + char *error; //!< The last error string created by one of the call backs. + bool established; //!< Set to false once the connection has been properly established. + CS_INT rows_affected; //!< Rows affected by last INSERT / UPDATE / DELETE. +} rlm_sql_freetds_conn_t; + +#define MAX_DATASTR_LEN 256 + +/** Client-Library error handler + * + * Callback for any errors raised by the Client-Library. Will overwrite any previous errors associated + * with a connection. + * + * @param context The FreeTDS library context. + * @param conn DB connection handle. + * @param emsgp Pointer to the error structure. + * @return CS_CUCCEED + */ +static CS_RETCODE CS_PUBLIC clientmsg_callback(CS_CONTEXT *context, UNUSED CS_CONNECTION *conn, CS_CLIENTMSG *emsgp) +{ + rlm_sql_freetds_conn_t *this = NULL; + int len = 0; + + /* + * Not actually an error, but the client wanted to tell us something... + */ + if (emsgp->severity == CS_SV_INFORM) { + INFO("rlm_sql_freetds: %s", emsgp->msgstring); + + return CS_SUCCEED; + } + + if ((cs_config(context, CS_GET, CS_USERDATA, &this, sizeof(this), &len) != CS_SUCCEED) || !this) { + ERROR("rlm_sql_freetds: failed retrieving context userdata"); + + return CS_SUCCEED; + } + + if (this->error) TALLOC_FREE(this->error); + + this->error = talloc_typed_asprintf(this, "client error: severity(%ld), number(%ld), origin(%ld), layer(%ld): %s", + (long)CS_SEVERITY(emsgp->severity), (long)CS_NUMBER(emsgp->msgnumber), + (long)CS_ORIGIN(emsgp->msgnumber), (long)CS_LAYER(emsgp->msgnumber), + emsgp->msgstring); + + if (emsgp->osstringlen > 0) { + this->error = talloc_asprintf_append(this->error, ". os error: number(%ld): %s", + (long)emsgp->osnumber, emsgp->osstring); + } + + return CS_SUCCEED; +} + +/** Client error handler + * + * Callback for any errors raised by the client. Will overwrite any previous errors associated + * with a connection. + * + * @param context The FreeTDS library context. + * @param emsgp Pointer to the error structure. + * @return CS_SUCCEED + */ +static CS_RETCODE CS_PUBLIC csmsg_callback(CS_CONTEXT *context, CS_CLIENTMSG *emsgp) +{ + rlm_sql_freetds_conn_t *this = NULL; + int len = 0; + + /* + * Not actually an error, but the client wanted to tell us something... + */ + if (emsgp->severity == CS_SV_INFORM) { + INFO("rlm_sql_freetds: %s", emsgp->msgstring); + + return CS_SUCCEED; + } + + if ((cs_config(context, CS_GET, CS_USERDATA, &this, sizeof(this), &len) != CS_SUCCEED) || !this) { + ERROR("rlm_sql_freetds: failed retrieving context userdata"); + + return CS_SUCCEED; + } + + if (this->error) TALLOC_FREE(this->error); + + this->error = talloc_typed_asprintf(this, "cs error: severity(%ld), number(%ld), origin(%ld), layer(%ld): %s", + (long)CS_SEVERITY(emsgp->severity), (long)CS_NUMBER(emsgp->msgnumber), + (long)CS_ORIGIN(emsgp->msgnumber), (long)CS_LAYER(emsgp->msgnumber), + emsgp->msgstring); + + if (emsgp->osstringlen > 0) { + this->error = talloc_asprintf_append(this->error, ". os error: number(%ld): %s", + (long)emsgp->osnumber, emsgp->osstring); + } + + return CS_SUCCEED; +} + +/** Server error handler + * + * Callback for any messages sent back from the server. + * + * There's no standard categorisation of messages sent back from the server, so we don't know they're errors, + * the only thing we can do is write them to the long as informational messages. + * + * @param context The FreeTDS library context. + * @param conn DB connection handle. + * @param msgp Pointer to the error structure. + * @return CS_SUCCEED + */ +static CS_RETCODE CS_PUBLIC servermsg_callback(CS_CONTEXT *context, UNUSED CS_CONNECTION *conn, CS_SERVERMSG *msgp) +{ + rlm_sql_freetds_conn_t *this = NULL; + int len = 0; + + if ((cs_config(context, CS_GET, CS_USERDATA, &this, sizeof(this), &len) != CS_SUCCEED) || !this) { + ERROR("rlm_sql_freetds: failed retrieving context userdata"); + + return CS_SUCCEED; + } + + /* + * Because apparently there are no standard severity levels *brilliant* + */ + if (this->established) { + INFO("rlm_sql_freetds: server msg from \"%s\": severity(%ld), number(%ld), origin(%ld), " + "layer(%ld), procedure \"%s\": %s", + (msgp->svrnlen > 0) ? msgp->svrname : "unknown", + (long)msgp->msgnumber, (long)msgp->severity, (long)msgp->state, (long)msgp->line, + (msgp->proclen > 0) ? msgp->proc : "none", msgp->text); + } else { + if (this->error) TALLOC_FREE(this->error); + + this->error = talloc_typed_asprintf(this, "Server msg from \"%s\": severity(%ld), number(%ld), " + "origin(%ld), layer(%ld), procedure \"%s\": %s", + (msgp->svrnlen > 0) ? msgp->svrname : "unknown", + (long)msgp->msgnumber, (long)msgp->severity, (long)msgp->state, + (long)msgp->line, + (msgp->proclen > 0) ? msgp->proc : "none", msgp->text); + } + + return CS_SUCCEED; +} + +/************************************************************************* + * + * Function: sql_query + * + * Purpose: Issue a non-SELECT query (ie: update/delete/insert) to + * the database. + * + *************************************************************************/ +static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config, char const *query) +{ + rlm_sql_freetds_conn_t *conn = handle->conn; + + CS_RETCODE results_ret; + CS_INT result_type; + + /* + * Reset rows_affected in case the query fails. + * Prevents accidentally returning the rows_affected from a previous query. + */ + conn->rows_affected = -1; + + if (ct_cmd_alloc(conn->db, &conn->command) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: Unable to allocate command structure (ct_cmd_alloc())"); + + return RLM_SQL_ERROR; + } + + if (ct_command(conn->command, CS_LANG_CMD, query, CS_NULLTERM, CS_UNUSED) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: Unable to initialise command structure (ct_command())"); + + return RLM_SQL_ERROR; + } + + if (ct_send(conn->command) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: Unable to send command (ct_send())"); + + return RLM_SQL_ERROR; + } + + /* + * We'll make three calls to ct_results, first to get a success indicator, secondly to get a + * done indicator, and thirdly to get a "nothing left to handle" status. + */ + + /* + * First call to ct_results, we need returncode CS_SUCCEED and result_type CS_CMD_SUCCEED. + */ + if ((results_ret = ct_results(conn->command, &result_type)) == CS_SUCCEED) { + if (result_type != CS_CMD_SUCCEED) { + if (result_type == CS_ROW_RESULT) { + ERROR("rlm_sql_freetds: sql_query processed a query returning rows. " + "Use sql_select_query instead!"); + } + ERROR("rlm_sql_freetds: Result failure or unexpected result type from query"); + + return RLM_SQL_ERROR; + } + } else { + switch (results_ret) { + case CS_FAIL: /* Serious failure, freetds requires us to cancel and maybe even close db */ + ERROR("rlm_sql_freetds: Failure retrieving query results"); + + if (ct_cancel(NULL, conn->command, CS_CANCEL_ALL) == CS_FAIL) { + INFO("rlm_sql_freetds: Cleaning up"); + + return RLM_SQL_RECONNECT; + } + conn->command = NULL; + + return RLM_SQL_ERROR; + default: + ERROR("rlm_sql_freetds: Unexpected return value from ct_results()"); + + return RLM_SQL_ERROR; + } + } + + /* + * Retrieve the number of rows affected - the later calls + * to ct_results end up resetting the underlying counter so we + * no longer have access to this. + */ + if (ct_res_info(conn->command, CS_ROW_COUNT, &conn->rows_affected, CS_UNUSED, NULL) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: error retrieving row count"); + + return RLM_SQL_ERROR; + } + + /* + * Second call to ct_results, we need returncode CS_SUCCEED + * and result_type CS_CMD_DONE. + */ + if ((results_ret = ct_results(conn->command, &result_type)) == CS_SUCCEED) { + if (result_type != CS_CMD_DONE) { + ERROR("rlm_sql_freetds: Result failure or unexpected result type from query"); + + return RLM_SQL_ERROR; + } + } else { + switch (results_ret) { + case CS_FAIL: /* Serious failure, freetds requires us to cancel and maybe even close db */ + ERROR("rlm_sql_freetds: Failure retrieving query results"); + if (ct_cancel(NULL, conn->command, CS_CANCEL_ALL) == CS_FAIL) return RLM_SQL_RECONNECT; + + conn->command = NULL; + return RLM_SQL_ERROR; + + default: + ERROR("rlm_sql_freetds: Unexpected return value from ct_results()"); + + return RLM_SQL_ERROR; + } + } + + /* + * Third call to ct_results, we need returncode CS_END_RESULTS result_type will be ignored. + */ + results_ret = ct_results(conn->command, &result_type); + switch (results_ret) { + case CS_FAIL: /* Serious failure, freetds requires us to cancel and maybe even close db */ + ERROR("rlm_sql_freetds: Failure retrieving query results"); + if (ct_cancel(NULL, conn->command, CS_CANCEL_ALL) == CS_FAIL) return RLM_SQL_RECONNECT; + conn->command = NULL; + + return RLM_SQL_ERROR; + + case CS_END_RESULTS: /* This is where we want to end up */ + break; + + default: + ERROR("rlm_sql_freetds: Unexpected return value from ct_results()"); + + return RLM_SQL_ERROR; + } + + return RLM_SQL_OK; +} + +/************************************************************************* + * + * Function: sql_num_fields + * + * Purpose: database specific num_fields function. Returns number + * of columns from query + * + *************************************************************************/ +static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) +{ + rlm_sql_freetds_conn_t *conn = handle->conn; + int num = 0; + + if (ct_res_info(conn->command, CS_NUMDATA, (CS_INT *)&num, CS_UNUSED, NULL) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: Error retrieving column count"); + + return RLM_SQL_ERROR; + } + + return num; +} + +/** 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(UNUSED TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen, + rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) +{ + rlm_sql_freetds_conn_t *conn = handle->conn; + + rad_assert(conn && conn->db); + rad_assert(outlen > 0); + + if (!conn->error) return 0; + + out[0].type = L_ERR; + out[0].msg = conn->error; + + return 1; +} + +static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) +{ + rlm_sql_freetds_conn_t *conn = handle->conn; + + ct_cancel(NULL, conn->command, CS_CANCEL_ALL); + if (ct_cmd_drop(conn->command) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: freeing command structure failed"); + + return RLM_SQL_ERROR; + } + conn->command = NULL; + + TALLOC_FREE(conn->results); + + return RLM_SQL_OK; + +} + +/** Execute a query when we expected a result set + * + * @note Only the first row from queries returning several rows will be returned by this function, + * consecutive rows will be discarded. + * + */ +static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query) +{ + rlm_sql_freetds_conn_t *conn = handle->conn; + + CS_RETCODE results_ret; + CS_INT result_type; + CS_DATAFMT descriptor; + + int colcount,i; + char **rowdata; + + if (!conn->db) { + ERROR("rlm_sql_freetds: socket not connected"); + + return RLM_SQL_ERROR; + } + + if (ct_cmd_alloc(conn->db, &conn->command) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: unable to allocate command structure (ct_cmd_alloc())"); + + return RLM_SQL_ERROR; + } + + if (ct_command(conn->command, CS_LANG_CMD, query, CS_NULLTERM, CS_UNUSED) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: unable to initiate command structure (ct_command()"); + + return RLM_SQL_ERROR; + } + + if (ct_send(conn->command) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: unable to send command (ct_send())"); + return RLM_SQL_ERROR; + } + + results_ret = ct_results(conn->command, &result_type); + switch (results_ret) { + case CS_SUCCEED: + switch (result_type) { + case CS_ROW_RESULT: + + /* + * Set up a target buffer for the results data, and associate the buffer with the results, + * but the actual fetching takes place in sql_fetch_row. + * The layer above MUST call sql_fetch_row and/or sql_finish_select_query + * or this socket will be unusable and may cause segfaults + * if reused later on. + */ + + /* + * Set up the DATAFMT structure that describes our target array + * and tells freetds what we want future ct_fetch calls to do. + */ + descriptor.datatype = CS_CHAR_TYPE; /* The target buffer is a string */ + descriptor.format = CS_FMT_NULLTERM; /* Null termination please */ + descriptor.maxlength = MAX_DATASTR_LEN; /* The string arrays are this large */ + descriptor.count = 1; /* Fetch one row of data */ + descriptor.locale = NULL; /* Don't do NLS stuff */ + + colcount = sql_num_fields(handle, config); /* Get number of elements in row result */ + + rowdata = talloc_zero_array(conn, char *, colcount + 1); /* Space for pointers */ + rowdata[colcount] = NULL; + + for (i = 0; i < colcount; i++) { + /* Space to hold the result data */ + rowdata[i] = talloc_array(rowdata, char, MAX_DATASTR_LEN + 1); + + /* Associate the target buffer with the data */ + if (ct_bind(conn->command, i + 1, &descriptor, rowdata[i], NULL, NULL) != CS_SUCCEED) { + talloc_free(rowdata); + + ERROR("rlm_sql_freetds: ct_bind() failed)"); + + return RLM_SQL_ERROR; + } + + } + + rowdata[i] = NULL; /* Terminate the array */ + conn->results = rowdata; + break; + + case CS_CMD_SUCCEED: + case CS_CMD_DONE: + ERROR("rlm_sql_freetds: query returned no data"); + break; + + default: + + ERROR("rlm_sql_freetds: unexpected result type from query"); + sql_finish_select_query(handle, config); + + return RLM_SQL_ERROR; + } + break; + + case CS_FAIL: + + /* + * Serious failure, freetds requires us to cancel the results and maybe even close the db. + */ + + ERROR("rlm_sql_freetds: failure retrieving query results"); + + if (ct_cancel(NULL, conn->command, CS_CANCEL_ALL) == CS_FAIL) { + ERROR("rlm_sql_freetds: cleaning up"); + + return RLM_SQL_RECONNECT; + } + conn->command = NULL; + + return RLM_SQL_ERROR; + + default: + ERROR("rlm_sql_freetds: unexpected return value from ct_results()"); + + return RLM_SQL_ERROR; + } + + return RLM_SQL_OK; +} + +static int sql_num_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) +{ + rlm_sql_freetds_conn_t *conn = handle->conn; + + return (conn->rows_affected); +} + +static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) +{ + rlm_sql_freetds_conn_t *conn = handle->conn; + CS_INT ret, count; + + handle->row = NULL; + + ret = ct_fetch(conn->command, CS_UNUSED, CS_UNUSED, CS_UNUSED, &count); + switch (ret) { + case CS_FAIL: + /* + * Serious failure, freetds requires us to cancel the results and maybe even close the db. + */ + ERROR("rlm_sql_freetds: failure fetching row data"); + if (ct_cancel(NULL, conn->command, CS_CANCEL_ALL) == CS_FAIL) { + ERROR("rlm_sql_freetds: cleaning up"); + } else { + conn->command = NULL; + } + + return RLM_SQL_RECONNECT; + + case CS_END_DATA: + return RLM_SQL_NO_MORE_ROWS; + + case CS_SUCCEED: + handle->row = conn->results; + + return RLM_SQL_OK; + + case CS_ROW_FAIL: + ERROR("rlm_sql_freetds: recoverable failure fetching row data"); + + return RLM_SQL_RECONNECT; + + default: + ERROR("rlm_sql_freetds: unexpected returncode from ct_fetch"); + + return RLM_SQL_ERROR; + } +} + +static sql_rcode_t sql_free_result(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) +{ + + /* + * Not implemented, never called from rlm_sql anyway result buffer is freed in the + * finish_query functions. + */ + return RLM_SQL_OK; + +} + +static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) +{ + rlm_sql_freetds_conn_t *conn = handle->conn; + + ct_cancel(NULL, conn->command, CS_CANCEL_ALL); + if (ct_cmd_drop(conn->command) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: freeing command structure failed"); + + return RLM_SQL_ERROR; + } + conn->command = NULL; + conn->rows_affected = -1; + + return RLM_SQL_OK; +} + +static int sql_affected_rows(rlm_sql_handle_t *handle, rlm_sql_config_t *config) +{ + return sql_num_rows(handle, config); +} + + +static int _sql_socket_destructor(rlm_sql_freetds_conn_t *conn) +{ + DEBUG2("rlm_sql_freetds: socket destructor called, closing socket"); + + if (conn->command) { + ct_cancel(NULL, conn->command, CS_CANCEL_ALL); + if (ct_cmd_drop(conn->command) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: freeing command structure failed"); + + return RLM_SQL_ERROR; + } + } + + if (conn->db) { + /* + * We first try gracefully closing the connection (which informs the server) + * Then if that fails we force the connection closure. + * + * Sybase docs says this may fail because of pending results, but we + * should not have any pending results at this point, so something else must + * of gone wrong. + */ + if (ct_close(conn->db, CS_UNUSED) != CS_SUCCEED) { + ct_close(conn->db, CS_FORCE_CLOSE); + } + + ct_con_drop(conn->db); + } + + if (conn->context) { + ct_exit(conn->context, CS_UNUSED); + cs_ctx_drop(conn->context); + } + + return RLM_SQL_OK; +} + +static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config) +{ + rlm_sql_freetds_conn_t *conn; + + MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_freetds_conn_t)); + talloc_set_destructor(conn, _sql_socket_destructor); + + /* + * Allocate a CS context structure. This should really only be done once, but because of + * the db pooling design of rlm_sql, we'll have to go with one context per db + */ + if (cs_ctx_alloc(CS_VERSION_100, &conn->context) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: unable to allocate CS context structure (cs_ctx_alloc())"); + + goto error; + } + + /* + * Initialize ctlib + */ + if (ct_init(conn->context, CS_VERSION_100) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: unable to initialize Client-Library"); + + goto error; + } + + /* + * Install callback functions for error-handling + */ + if (cs_config(conn->context, CS_SET, CS_MESSAGE_CB, (CS_VOID *)csmsg_callback, CS_UNUSED, NULL) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: unable to install CS Library error callback"); + + goto error; + } + + if (cs_config(conn->context, CS_SET, CS_USERDATA, + (CS_VOID *)&handle->conn, sizeof(handle->conn), NULL) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: unable to set userdata pointer"); + + goto error; + } + + if (ct_callback(conn->context, NULL, CS_SET, CS_CLIENTMSG_CB, (CS_VOID *)clientmsg_callback) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: unable to install client message callback"); + + goto error; + } + + if (ct_callback(conn->context, NULL, CS_SET, CS_SERVERMSG_CB, (CS_VOID *)servermsg_callback) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: unable to install server message callback"); + + goto error; + } + + /* + * Allocate a ctlib db structure + */ + if (ct_con_alloc(conn->context, &conn->db) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: unable to allocate db structure"); + + goto error; + } + + /* + * Set User and Password properties for the db + */ + { + CS_VOID *login, *password; + CS_CHAR *server; + char database[128]; + + memcpy(&login, &config->sql_login, sizeof(login)); + if (ct_con_props(conn->db, CS_SET, CS_USERNAME, login, strlen(config->sql_login), NULL) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: unable to set username for db"); + + goto error; + } + + memcpy(&password, &config->sql_password, sizeof(password)); + if (ct_con_props(conn->db, CS_SET, CS_PASSWORD, + password, strlen(config->sql_password), NULL) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: unable to set password for db"); + + goto error; + } + + /* + * Connect to the database + */ + memcpy(&server, &config->sql_server, sizeof(server)); + if (ct_connect(conn->db, server, strlen(config->sql_server)) != CS_SUCCEED) { + ERROR("rlm_sql_freetds: unable to establish db to symbolic servername %s", + config->sql_server); + + goto error; + } + + /* + * There doesn't appear to be a way to set the database with the API, so use an + * sql statement when we first open the connection. + */ + snprintf(database, sizeof(database), "USE %s;", config->sql_db); + if (sql_query(handle, config, database) != RLM_SQL_OK) { + goto error; + } + + sql_finish_query(handle, config); + } + + return RLM_SQL_OK; + +error: + if (conn->context) { + sql_log_entry_t error; + + if (sql_error(NULL, &error, 1, handle, config) > 0) ERROR("rlm_sql_freetds: %s", error.msg); + } + + return RLM_SQL_ERROR; +} + +/* Exported to rlm_sql */ +extern rlm_sql_module_t rlm_sql_freetds; +rlm_sql_module_t rlm_sql_freetds = { + .name = "rlm_sql_freetds", + .sql_socket_init = sql_socket_init, + .sql_query = sql_query, + .sql_select_query = sql_select_query, + .sql_num_fields = sql_num_fields, + .sql_num_rows = sql_num_rows, + .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 +}; -- cgit v1.2.3