2 * sql_iodbc.c iODBC support for FreeRadius
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2000,2006 The FreeRADIUS server project
21 * Copyright 2000 Jeff Carneal <jeff@apex.net>
25 USES_APPLE_DEPRECATED_API
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/rad_assert.h>
38 #define IODBC_MAX_ERROR_LEN 256
40 typedef struct rlm_sql_iodbc_conn {
48 struct sql_socket *next;
51 } rlm_sql_iodbc_conn_t;
53 static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
54 rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config);
55 static int sql_num_fields(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
57 static int _sql_socket_destructor(rlm_sql_iodbc_conn_t *conn)
59 DEBUG2("rlm_sql_iodbc: Socket destructor called, closing socket");
61 if (conn->stmt) SQLFreeStmt(conn->stmt, SQL_DROP);
63 if (conn->dbc_handle) {
64 SQLDisconnect(conn->dbc_handle);
65 SQLFreeConnect(conn->dbc_handle);
68 if (conn->env_handle) SQLFreeEnv(conn->env_handle);
73 static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
76 rlm_sql_iodbc_conn_t *conn;
78 sql_log_entry_t entry;
80 MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_iodbc_conn_t));
81 talloc_set_destructor(conn, _sql_socket_destructor);
83 rcode = SQLAllocEnv(&conn->env_handle);
84 if (!SQL_SUCCEEDED(rcode)) {
85 ERROR("rlm_sql_iodbc: SQLAllocEnv failed");
86 if (sql_error(NULL, &entry, 1, handle, config) > 0) ERROR("rlm_sql_iodbc: %s", entry.msg);
91 rcode = SQLAllocConnect(conn->env_handle,
93 if (!SQL_SUCCEEDED(rcode)) {
94 ERROR("rlm_sql_iodbc: SQLAllocConnect failed");
95 if (sql_error(NULL, &entry, 1, handle, config) > 0) ERROR("rlm_sql_iodbc: %s", entry.msg);
101 * The iodbc API doesn't qualify arguments as const even when they should be.
104 SQLCHAR *server, *login, *password;
106 memcpy(&server, &config->sql_server, sizeof(server));
107 memcpy(&login, &config->sql_login, sizeof(login));
108 memcpy(&password, &config->sql_password, sizeof(password));
110 rcode = SQLConnect(conn->dbc_handle, server, SQL_NTS, login, SQL_NTS, password, SQL_NTS);
112 if (!SQL_SUCCEEDED(rcode)) {
113 ERROR("rlm_sql_iodbc: SQLConnectfailed");
114 if (sql_error(NULL, &entry, 1, handle, config) > 0) ERROR("rlm_sql_iodbc: %s", entry.msg);
116 return RLM_SQL_ERROR;
122 static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config, char const *query)
124 rlm_sql_iodbc_conn_t *conn = handle->conn;
127 rcode = SQLAllocStmt(conn->dbc_handle, &conn->stmt);
128 if (!SQL_SUCCEEDED(rcode)) return RLM_SQL_ERROR;
130 if (!conn->dbc_handle) {
131 ERROR("rlm_sql_iodbc: Socket not connected");
132 return RLM_SQL_ERROR;
138 memcpy(&statement, &query, sizeof(statement));
139 rcode = SQLExecDirect(conn->stmt, statement, SQL_NTS);
142 if (!SQL_SUCCEEDED(rcode)) return RLM_SQL_ERROR;
147 static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query)
153 rlm_sql_iodbc_conn_t *conn = handle->conn;
155 if (sql_query(handle, config, query) < 0) return RLM_SQL_ERROR;
157 numfields = sql_num_fields(handle, config);
159 row = (char **) rad_malloc(sizeof(char *) * (numfields+1));
160 memset(row, 0, (sizeof(char *) * (numfields)));
161 row[numfields] = NULL;
163 for(i=1; i<=numfields; i++) {
164 SQLColAttributes(conn->stmt, ((SQLUSMALLINT) i), SQL_COLUMN_LENGTH, NULL, 0, NULL, &len);
168 * Allocate space for each column
170 row[i - 1] = rad_malloc((size_t) len);
173 * This makes me feel dirty, but, according to Microsoft, it works.
174 * Any ODBC datatype can be converted to a 'char *' according to
177 * http://msdn.microsoft.com/library/psdk/dasdk/odap4o4z.htm
179 SQLBindCol(conn->stmt, i, SQL_C_CHAR, (SQLCHAR *)row[i-1], len, 0);
187 static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
191 rlm_sql_iodbc_conn_t *conn = handle->conn;
193 SQLNumResultCols(conn->stmt, &count);
198 static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
200 rlm_sql_iodbc_conn_t *conn = handle->conn;
202 SQLSMALLINT fields, len, i;
207 SQLNumResultCols(conn->stmt, &fields);
208 if (fields == 0) return RLM_SQL_ERROR;
210 MEM(names = talloc_array(handle, char const *, fields));
212 for (i = 0; i < fields; i++) {
215 switch (SQLColAttribute(conn->stmt, i, SQL_DESC_BASE_COLUMN_NAME,
216 field, sizeof(field), &len, NULL)) {
217 case SQL_INVALID_HANDLE:
219 ERROR("Failed retrieving field name at index %i", i);
221 return RLM_SQL_ERROR;
227 MEM(p = talloc_array(names, char, (size_t)len + 1));
228 strlcpy(p, field, (size_t)len + 1);
236 static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
239 rlm_sql_iodbc_conn_t *conn = handle->conn;
243 rc = SQLFetch(conn->stmt);
244 if (rc == SQL_NO_DATA_FOUND) return RLM_SQL_NO_MORE_ROWS;
246 /* XXX Check rc for database down, if so, return RLM_SQL_RECONNECT */
248 handle->row = conn->row;
252 static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
255 rlm_sql_iodbc_conn_t *conn = handle->conn;
257 for (i = 0; i < sql_num_fields(handle, config); i++) free(conn->row[i]);
261 SQLFreeStmt(conn->stmt, SQL_DROP);
266 /** Retrieves any errors associated with the connection handle
268 * @note Caller will free any memory allocated in ctx.
270 * @param ctx to allocate temporary error buffers in.
271 * @param out Array of sql_log_entrys to fill.
272 * @param outlen Length of out array.
273 * @param handle rlm_sql connection handle.
274 * @param config rlm_sql config.
275 * @return number of errors written to the sql_log_entry array.
277 static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
278 rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
280 rlm_sql_iodbc_conn_t *conn = handle->conn;
281 SQLINTEGER errornum = 0;
282 SQLSMALLINT length = 0;
283 SQLCHAR state[256] = "";
284 SQLCHAR errbuff[IODBC_MAX_ERROR_LEN];
286 rad_assert(outlen > 0);
289 SQLError(conn->env_handle, conn->dbc_handle, conn->stmt,
290 state, &errornum, errbuff, IODBC_MAX_ERROR_LEN, &length);
291 if (errbuff[0] == '\0') return 0;
294 out[0].msg = talloc_asprintf(ctx, "%s: %s", state, errbuff);
299 static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
301 return sql_free_result(handle, config);
304 static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
306 return sql_free_result(handle, config);
309 static int sql_affected_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
312 rlm_sql_iodbc_conn_t *conn = handle->conn;
314 SQLRowCount(conn->stmt, &count);
318 /* Exported to rlm_sql */
319 extern rlm_sql_module_t rlm_sql_iodbc;
320 rlm_sql_module_t rlm_sql_iodbc = {
321 .name = "rlm_sql_iodbc",
322 .sql_socket_init = sql_socket_init,
323 .sql_query = sql_query,
324 .sql_select_query = sql_select_query,
325 .sql_num_fields = sql_num_fields,
326 .sql_affected_rows = sql_affected_rows,
327 .sql_fields = sql_fields,
328 .sql_fetch_row = sql_fetch_row,
329 .sql_free_result = sql_free_result,
330 .sql_error = sql_error,
331 .sql_finish_query = sql_finish_query,
332 .sql_finish_select_query = sql_finish_select_query