* by Joerg Wendland <wendland@scan-plus.de>
*/
-#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/rad_assert.h>
#include <sys/stat.h>
#include <sqlcli.h>
#include "rlm_sql.h"
-typedef struct rlm_sql_db2_sock {
- SQLHANDLE hdbc;
- SQLHANDLE henv;
+typedef struct rlm_sql_conn {
+ SQLHANDLE dbc_handle;
+ SQLHANDLE env_handle;
SQLHANDLE stmt;
-} rlm_sql_db2_sock;
+} rlm_sql_db2_conn_t;
-/*************************************************************************
- *
- * Function: sql_create_socket
- *
- * Purpose: Establish connection to the db
- *
- *************************************************************************/
-static int sql_init_socket(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
+static int _sql_socket_destructor(rlm_sql_db2_conn_t *conn)
{
- SQLRETURN retval;
- rlm_sql_db2_sock *sock;
+ DEBUG2("rlm_sql_db2: Socket destructor called, closing socket");
- /* allocate socket */
- if (!handle->conn) {
- handle->conn = (rlm_sql_db2_sock*)rad_malloc(sizeof(rlm_sql_db2_sock));
- if (!handle->conn) {
- return -1;
- }
- }
- sock = handle->conn;
- memset(sock, 0, sizeof(*sock));
-
- /* allocate handles */
- SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &(sock->henv));
- SQLAllocHandle(SQL_HANDLE_DBC, sock->henv, &(sock->hdbc));
-
- /* connect to database */
- retval = SQLConnect(sock->hdbc,
- config->sql_server, SQL_NTS,
- config->sql_login, SQL_NTS,
- config->sql_password, SQL_NTS);
- if(retval != SQL_SUCCESS) {
- radlog(L_ERR, "could not connect to DB2 server %s\n", config->sql_server);
- SQLDisconnect(sock->hdbc);
- SQLFreeHandle(SQL_HANDLE_DBC, sock->hdbc);
- SQLFreeHandle(SQL_HANDLE_ENV, sock->henv);
- return -1;
+ if (conn->dbc_handle) {
+ SQLDisconnect(conn->dbc_handle);
+ SQLFreeHandle(SQL_HANDLE_DBC, conn->dbc_handle);
}
- return 0;
-}
+ if (conn->env_handle) SQLFreeHandle(SQL_HANDLE_ENV, conn->env_handle);
+ return RLM_SQL_OK;
+}
-/*************************************************************************
- *
- * Function: sql_destroy_socket
- *
- * Purpose: Free socket and private connection data
- *
- *************************************************************************/
-static int sql_destroy_socket(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
+static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
{
- free(handle->conn);
- handle->conn = NULL;
- return 0;
+ SQLRETURN retval;
+ rlm_sql_db2_conn_t *conn;
+
+ MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_db2_conn_t));
+ talloc_set_destructor(conn, _sql_socket_destructor);
+
+ /* Allocate handles */
+ SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &(conn->env_handle));
+ SQLAllocHandle(SQL_HANDLE_DBC, conn->env_handle, &(conn->dbc_handle));
+
+ /*
+ * The db2 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));
+
+ retval = SQLConnect(conn->dbc_handle,
+ server, SQL_NTS,
+ login, SQL_NTS,
+ password, SQL_NTS);
+ }
+
+ if (retval != SQL_SUCCESS) {
+ ERROR("could not connect to DB2 server %s", config->sql_server);
+
+ return RLM_SQL_ERROR;
+ }
+
+ return RLM_SQL_OK;
}
-/*************************************************************************
- *
- * Function: sql_query
- *
- * Purpose: Issue a query to the database
- *
- *************************************************************************/
-static int sql_query(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *config, char *querystr)
+static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config, char const *query)
{
SQLRETURN retval;
- rlm_sql_db2_sock *sock;
+ rlm_sql_db2_conn_t *conn;
- sock = handle->conn;
+ conn = handle->conn;
/* allocate handle for statement */
- SQLAllocHandle(SQL_HANDLE_STMT, sock->hdbc,
- &(sock->stmt));
+ SQLAllocHandle(SQL_HANDLE_STMT, conn->dbc_handle, &(conn->stmt));
/* execute query */
- retval = SQLExecDirect(sock->stmt, querystr, SQL_NTS);
- if(retval != SQL_SUCCESS) {
- /* XXX Check if retval means we should return SQL_DOWN */
- radlog(L_ERR, "could not execute statement \"%s\"\n", querystr);
- return -1;
+ {
+ SQLCHAR *db2_query;
+ memcpy(&db2_query, &query, sizeof(query));
+
+ retval = SQLExecDirect(conn->stmt, db2_query, SQL_NTS);
+ if(retval != SQL_SUCCESS) {
+ /* XXX Check if retval means we should return RLM_SQL_RECONNECT */
+ ERROR("Could not execute statement \"%s\"", query);
+ return RLM_SQL_ERROR;
+ }
}
- return 0;
+ return RLM_SQL_OK;
}
-
-/*************************************************************************
- *
- * Function: sql_select_query
- *
- * Purpose: Issue a select query to the database
- *
- *************************************************************************/
-static int sql_select_query(rlm_sql_handle_t * handle, rlm_sql_config_t *config, char *querystr)
+static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query)
{
- return sql_query(handle, config, querystr);
+ return sql_query(handle, config, query);
}
-
-/*************************************************************************
- *
- * 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)
+static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
{
SQLSMALLINT c;
- rlm_sql_db2_sock *sock;
+ rlm_sql_db2_conn_t *conn;
- sock = handle->conn;
- SQLNumResultCols(sock->stmt, &c);
+ conn = handle->conn;
+ SQLNumResultCols(conn->stmt, &c);
return c;
}
+static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
+{
+ rlm_sql_db2_conn_t *conn = handle->conn;
-/*************************************************************************
- *
- * Function: sql_fetch_row
- *
- * Purpose: database specific fetch_row. Returns a rlm_sql_row_t struct
- * with all the data for the query in 'handle->row'. Returns
- * 0 on success, -1 on failure, SQL_DOWN if 'database is down'
- *
- *************************************************************************/
-static int sql_fetch_row(rlm_sql_handle_t * handle, rlm_sql_config_t *config)
+ SQLSMALLINT fields, len, i;
+
+ char const **names;
+ char field[128];
+
+ SQLNumResultCols(conn->stmt, &fields);
+ if (fields == 0) return RLM_SQL_ERROR;
+
+ MEM(names = talloc_array(handle, char const *, fields));
+
+ for (i = 0; i < fields; i++) {
+ char *p;
+
+ switch (SQLColAttribute(conn->stmt, i, SQL_DESC_BASE_COLUMN_NAME,
+ field, sizeof(field), &len, NULL)) {
+ case SQL_INVALID_HANDLE:
+ case SQL_ERROR:
+ ERROR("Failed retrieving field name at index %i", i);
+ talloc_free(names);
+ return RLM_SQL_ERROR;
+
+ default:
+ break;
+ }
+
+ MEM(p = talloc_array(names, char, (size_t)len + 1));
+ strlcpy(p, field, (size_t)len + 1);
+ names[i] = p;
+ }
+ *out = names;
+
+ return RLM_SQL_OK;
+}
+
+static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
{
int c, i;
SQLINTEGER len, slen;
rlm_sql_row_t retval;
- rlm_sql_db2_sock *sock;
+ rlm_sql_db2_conn_t *conn;
- sock = handle->conn;
+ conn = handle->conn;
c = sql_num_fields(handle, config);
retval = (rlm_sql_row_t)rad_malloc(c*sizeof(char*)+1);
memset(retval, 0, c*sizeof(char*)+1);
/* advance cursor */
- if(SQLFetch(sock->stmt) == SQL_NO_DATA_FOUND) {
+ if (SQLFetch(conn->stmt) == SQL_NO_DATA_FOUND) {
handle->row = NULL;
- goto error;
+ for (i = 0; i < c; i++) free(retval[i]);
+ free(retval);
+ return RLM_SQL_NO_MORE_ROWS;
}
- for(i = 0; i < c; i++) {
+ for (i = 0; i < c; i++) {
/* get column length */
- SQLColAttribute(sock->stmt,
- i+1, SQL_DESC_DISPLAY_SIZE,
- NULL, 0, NULL, &len);
- retval[i] = (char*)rad_malloc(len+1);
+ SQLColAttribute(conn->stmt, i+1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &len);
+
+ retval[i] = rad_malloc(len+1);
+
/* get the actual column */
- SQLGetData(sock->stmt,
- i+1, SQL_C_CHAR, retval[i], len+1, &slen);
- if(slen == SQL_NULL_DATA)
+ SQLGetData(conn->stmt, i + 1, SQL_C_CHAR, retval[i], len+1, &slen);
+ if(slen == SQL_NULL_DATA) {
retval[i][0] = '\0';
+ }
}
handle->row = retval;
- return 0;
-
-error:
- for(i = 0; i < c; i++) {
- free(retval[i]);
- }
- free(retval);
- return -1;
+ return RLM_SQL_OK;
}
-/*************************************************************************
- *
- * Function: sql_free_result
- *
- * Purpose: database specific free_result. Frees memory allocated
- * for a result set
- *
- *************************************************************************/
-static int sql_free_result(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *config)
+static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
{
- rlm_sql_db2_sock *sock;
- sock = handle->conn;
- SQLFreeHandle(SQL_HANDLE_STMT, sock->stmt);
- return 0;
-}
-
+ rlm_sql_db2_conn_t *conn;
+ conn = handle->conn;
+ SQLFreeHandle(SQL_HANDLE_STMT, conn->stmt);
+ return RLM_SQL_OK;
+}
-/*************************************************************************
- *
- * Function: sql_error
+/** Retrieves any errors associated with the connection handle
*
- * Purpose: database specific error. Returns error associated with
- * connection
+ * @note Caller will free any memory allocated in ctx.
*
- *************************************************************************/
-static const char *sql_error(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *config)
+ * @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)
{
- /* this should really be enough, if not, you still got the sqlstate */
-#define MSGLEN 512
- char sqlstate[6];
- char msg[MSGLEN];
- char *retval;
- SQLINTEGER err;
- SQLSMALLINT rl;
- rlm_sql_db2_sock *sock;
-
- sock = handle->conn;
-
- SQLGetDiagRec(SQL_HANDLE_STMT, sock->stmt,
- 1, sqlstate, &err, msg, MSGLEN, &rl);
- retval = (char*)rad_malloc(strlen(msg)+20);
- sprintf(retval, "SQLSTATE %s: %s", sqlstate, msg);
- return retval;
-}
+ char state[6];
+ char errbuff[1024];
+ SQLINTEGER err;
+ SQLSMALLINT rl;
+ rlm_sql_db2_conn_t *conn = handle->conn;
+ rad_assert(conn);
+ rad_assert(outlen > 0);
-/*************************************************************************
- *
- * Function: sql_close
- *
- * Purpose: database specific close. Closes an open database
- * connection
- *
- *************************************************************************/
-static int sql_close(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *config)
-{
- rlm_sql_db2_sock *sock;
+ errbuff[0] = '\0';
+ SQLGetDiagRec(SQL_HANDLE_STMT, conn->stmt, 1, (SQLCHAR *) state, &err,
+ (SQLCHAR *) errbuff, sizeof(errbuff), &rl);
+ if (errbuff[0] == '\0') return 0;
- sock = handle->conn;
+ out[0].type = L_ERR;
+ out[0].msg = talloc_asprintf(ctx, "%s: %s", state, errbuff);
- SQLFreeHandle(SQL_HANDLE_DBC, sock->hdbc);
- SQLFreeHandle(SQL_HANDLE_ENV, sock->henv);
- return 0;
+ return 1;
}
-
-/*************************************************************************
- *
- * Function: sql_finish_query
- *
- * Purpose: End the query, such as freeing memory
- *
- *************************************************************************/
-static int sql_finish_query(UNUSED rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *config)
+static sql_rcode_t sql_finish_query(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
{
- return 0;
+ return RLM_SQL_OK;
}
-
-
-/*************************************************************************
- *
- * Function: sql_finish_select_query
- *
- * Purpose: End the select query, such as freeing memory or result
- *
- *************************************************************************/
-static int sql_finish_select_query(rlm_sql_handle_t * handle, rlm_sql_config_t *config)
+static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
{
return sql_finish_query(handle, config);
}
-
-/*************************************************************************
- *
- * Function: sql_affected_rows
- *
- * Purpose: Return the number of rows affected by the last query.
- *
- *************************************************************************/
-static int sql_affected_rows(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *config)
+static int sql_affected_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
{
SQLINTEGER c;
- rlm_sql_db2_sock *sock;
+ rlm_sql_db2_conn_t *conn = handle->conn;
- sock = handle->conn;
+ SQLRowCount(conn->stmt, &c);
- SQLRowCount(sock->stmt, &c);
return c;
}
-
-static int
-not_implemented(UNUSED rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *config)
-{
- radlog(L_ERR, "sql_db2: calling unimplemented function");
- exit(1);
-}
-
-
/* Exported to rlm_sql */
+extern rlm_sql_module_t rlm_sql_db2;
rlm_sql_module_t rlm_sql_db2 = {
- "rlm_sql_db2",
- NULL,
- sql_init_socket,
- sql_destroy_socket, /* sql_destroy_socket*/
- sql_query,
- sql_select_query,
- not_implemented, /* sql_store_result */
- sql_num_fields,
- not_implemented, /* sql_num_rows */
- sql_fetch_row,
- sql_free_result,
- sql_error,
- sql_close,
- sql_finish_query,
- sql_finish_select_query,
- sql_affected_rows,
+ .name = "rlm_sql_db2",
+ .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_fields = sql_fields,
+ .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
};