1 /***************************************************************************
2 * iODBC support for FreeRadius
3 * www.iodbc.org - iODBC info
4 * Jeff Carneal - Author of this module driver <jeff@apex.net>
5 ***************************************************************************/
12 #include "sql_iodbc.h"
14 /*************************************************************************
16 * Function: sql_init_socket
18 * Purpose: Establish connection to the db
20 *************************************************************************/
21 int sql_init_socket(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
23 rlm_sql_iodbc_sock *iodbc_sock;
25 sqlsocket->conn = (rlm_sql_iodbc_sock *)rad_malloc(sizeof(rlm_sql_iodbc_sock));
27 iodbc_sock = sqlsocket->conn;
29 if(SQLAllocEnv(&iodbc_sock->env_handle) != SQL_SUCCESS) {
30 radlog(L_CONS|L_ERR, "sql_create_socket: SQLAllocEnv failed: %s",
31 sql_error(sqlsocket, config));
35 if(SQLAllocConnect(iodbc_sock->env_handle, &iodbc_sock->dbc_handle) != SQL_SUCCESS) {
36 radlog(L_CONS|L_ERR, "sql_create_socket: SQLAllocConnect failed: %s",
37 sql_error(sqlsocket, config));
41 if (SQLConnect(iodbc_sock->dbc_handle, config->sql_db, SQL_NTS,
42 config->sql_login, SQL_NTS, config->sql_password,
43 SQL_NTS) != SQL_SUCCESS) {
44 radlog(L_CONS|L_ERR, "sql_create_socket: SQLConnectfailed: %s",
45 sql_error(sqlsocket, config));
49 if(SQLAllocStmt(iodbc_sock->dbc_handle, &iodbc_sock->stmt_handle) != SQL_SUCCESS) {
50 radlog(L_CONS|L_ERR, "sql_create_socket: SQLAllocStmt failed: %s",
51 sql_error(sqlsocket, config));
58 /*************************************************************************
62 * Purpose: Issue a non-SELECT query (ie: update/delete/insert) to
65 *************************************************************************/
66 int sql_query(SQLSOCK *sqlsocket, SQL_CONFIG *config, char *querystr) {
68 rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
71 radlog(L_DBG, "rlm_sql: %s", querystr);
72 if (iodbc_sock->dbc_handle == NULL) {
73 radlog(L_ERR, "sql_query: Socket not connected");
77 if(SQLExecDirect(iodbc_sock->stmt_handle, querystr, SQL_NTS) != SQL_SUCCESS) {
78 radlog(L_CONS|L_ERR, "sql_query: failed: %s",
79 sql_error(sqlsocket, config));
87 /*************************************************************************
89 * Function: sql_select_query
91 * Purpose: Issue a select query to the database
93 *************************************************************************/
94 int sql_select_query(SQLSOCK *sqlsocket, SQL_CONFIG *config, char *querystr) {
100 rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
102 if(sql_query(sqlsocket, config, querystr) < 0) {
106 numfields = sql_num_fields(sqlsocket, config);
108 row = (char **) rad_malloc(sizeof(char *) * numfields);
109 memset(row, 0, (sizeof(char *) * (numfields)));
110 row[numfields-1] = NULL;
112 for(i=1; i<=numfields; i++) {
113 SQLColAttributes(iodbc_sock->stmt_handle, ((SQLUSMALLINT) i), SQL_COLUMN_LENGTH,
114 NULL, 0, NULL, &len);
118 * Allocate space for each column
120 row[i-1] = (SQLCHAR*)rad_malloc((int)len);
123 * This makes me feel dirty, but, according to Microsoft, it works.
124 * Any ODBC datatype can be converted to a 'char *' according to
127 * http://msdn.microsoft.com/library/psdk/dasdk/odap4o4z.htm
129 SQLBindCol(iodbc_sock->stmt_handle, i, SQL_C_CHAR, (SQLCHAR *)row[i-1], len, 0);
132 iodbc_sock->row = row;
138 /*************************************************************************
140 * Function: sql_store_result
142 * Purpose: database specific store_result function. Returns a result
145 *************************************************************************/
146 int sql_store_result(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
152 /*************************************************************************
154 * Function: sql_num_fields
156 * Purpose: database specific num_fields function. Returns number
157 * of columns from query
159 *************************************************************************/
160 int sql_num_fields(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
163 rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
165 SQLNumResultCols(iodbc_sock->stmt_handle, (SQLSMALLINT *)&count);
170 /*************************************************************************
172 * Function: sql_num_rows
174 * Purpose: database specific num_rows. Returns number of rows in
177 *************************************************************************/
178 int sql_num_rows(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
180 * I presume this function is used to determine the number of
181 * rows in a result set *before* fetching them. I don't think
182 * this is possible in ODBC 2.x, but I'd be happy to be proven
183 * wrong. If you know how to do this, email me at jeff@apex.net
189 /*************************************************************************
191 * Function: sql_fetch_row
193 * Purpose: database specific fetch_row. Returns a SQL_ROW struct
194 * with all the data for the query
196 *************************************************************************/
197 SQL_ROW sql_fetch_row(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
200 rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
202 if((rc = SQLFetch(iodbc_sock->stmt_handle)) == SQL_NO_DATA_FOUND) {
210 /*************************************************************************
212 * Function: sql_free_result
214 * Purpose: database specific free_result. Frees memory allocated
217 *************************************************************************/
218 void sql_free_result(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
221 rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
223 for(i=0; i<sql_num_fields(sqlsocket, config); i++) {
224 free(iodbc_sock->row[i]);
227 iodbc_sock->row=NULL;
231 /*************************************************************************
233 * Function: sql_error
235 * Purpose: database specific error. Returns error associated with
238 *************************************************************************/
239 char *sql_error(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
241 SQLINTEGER errornum = 0;
242 SQLSMALLINT length = 0;
243 SQLCHAR state[256] = "";
244 static SQLCHAR error[256] = "";
245 rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
247 SQLError(idobc_sock->env_handle, iodbc_sock->dbc_handle, iodbc_sock->stmt_handle,
248 state, &errornum, error, 256, &length);
253 /*************************************************************************
255 * Function: sql_close
257 * Purpose: database specific close. Closes an open database
258 * connection and cleans up any open handles.
260 *************************************************************************/
261 int sql_close(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
263 rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
265 SQLFreeStmt(iodbc_sock->stmt_handle, SQL_DROP);
266 SQLDisconnect(iodbc_sock->dbc_handle);
267 SQLFreeConnect(iodbc_sock->dbc_handle);
268 SQLFreeEnv(iodbc_sock->env_handle);
270 iodbc_sock->stmt_handle = NULL;
271 iodbc_sock->dbc_handle = NULL;
272 iodbc_sock->env_handle = NULL;
278 /*************************************************************************
280 * Function: sql_finish_query
282 * Purpose: End the query, such as freeing memory
284 *************************************************************************/
285 int sql_finish_query(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
287 return sql_free_result(sqlsocket, config);
292 /*************************************************************************
294 * Function: sql_finish_select_query
296 * Purpose: End the select query, such as freeing memory or result
298 *************************************************************************/
299 int sql_finish_select_query(SQLSOCK *socket) {
304 /*************************************************************************
306 * Function: sql_affected_rows
308 * Purpose: Return the number of rows affected by the query (update,
311 *************************************************************************/
312 int sql_affected_rows(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
315 rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
317 SQLRowCount(iodbc_sock->stmt_handle, &count);
322 /*************************************************************************
324 * Function: sql_escape_string
326 * Purpose: Esacpe "'" and any other wierd charactors
328 *************************************************************************/
329 int sql_escape_string(SQLSOCK *sqlsocket, SQL_CONFIG *config, char *to, char *from, int length) {
332 for(x=0, y=0; (x < length) && (from[x]!='\0'); x++) {
371 /* Exported to rlm_sql */
372 rlm_sql_module_t rlm_sql_iodbc = {
386 sql_finish_select_query,