Pull fix from branch_1_1, so proxied EAP replies work
[freeradius.git] / src / modules / rlm_sql / drivers / rlm_sql_iodbc / sql_iodbc.c
1 /*
2  * sql_iodbc.c  iODBC support for FreeRadius
3  *
4  * Version:     $Id$
5  *
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.
10  *
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.
15  *
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
19  *
20  * Copyright 2000,2006  The FreeRADIUS server project
21  * Copyright 2000  Jeff Carneal <jeff@apex.net>
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <freeradius-devel/autoconf.h>
28
29 #include <stdio.h>
30 #include <sys/stat.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include <isql.h>
35 #include <isqlext.h>
36 #include <sqltypes.h>
37
38 #include <freeradius-devel/radiusd.h>
39 #include "rlm_sql.h"
40
41 typedef struct rlm_sql_iodbc_sock {
42         HENV    env_handle;
43         HDBC    dbc_handle;
44         HSTMT   stmt_handle;
45         int             id;
46         SQL_ROW row;
47
48         struct sql_socket *next;
49
50         void    *conn;
51 } rlm_sql_iodbc_sock;;
52
53 static char *sql_error(SQLSOCK *sqlsocket, SQL_CONFIG *config);
54 static int sql_num_fields(SQLSOCK *sqlsocket, SQL_CONFIG *config);
55
56 /*************************************************************************
57  *
58  *      Function: sql_init_socket
59  *
60  *      Purpose: Establish connection to the db
61  *
62  *************************************************************************/
63 static int sql_init_socket(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
64
65         rlm_sql_iodbc_sock *iodbc_sock;
66         SQLRETURN rcode;
67
68         if (!sqlsocket->conn) {
69                 sqlsocket->conn = (rlm_sql_iodbc_sock *)rad_malloc(sizeof(rlm_sql_iodbc_sock));
70                 if (!sqlsocket->conn) {
71                         return -1;
72                 }
73         }
74         iodbc_sock = sqlsocket->conn;
75         memset(iodbc_sock, 0, sizeof(*iodbc_sock));
76
77         rcode = SQLAllocEnv(&iodbc_sock->env_handle);
78         if (!SQL_SUCCEEDED(rcode)) {
79                 radlog(L_CONS|L_ERR, "sql_create_socket: SQLAllocEnv failed:  %s",
80                                 sql_error(sqlsocket, config));
81                 return -1;
82         }
83
84         rcode = SQLAllocConnect(iodbc_sock->env_handle,
85                                 &iodbc_sock->dbc_handle);
86         if (!SQL_SUCCEEDED(rcode)) {
87                 radlog(L_CONS|L_ERR, "sql_create_socket: SQLAllocConnect failed:  %s",
88                                 sql_error(sqlsocket, config));
89                 return -1;
90         }
91
92         rcode = SQLConnect(iodbc_sock->dbc_handle, config->sql_db,
93                            SQL_NTS, config->sql_login, SQL_NTS,
94                            config->sql_password, SQL_NTS);
95         if (!SQL_SUCCEEDED(rcode)) {
96                 radlog(L_CONS|L_ERR, "sql_create_socket: SQLConnectfailed:  %s",
97                                 sql_error(sqlsocket, config));
98                 return -1;
99         }
100
101         return 0;
102 }
103
104 /*************************************************************************
105  *
106  *      Function: sql_destroy_socket
107  *
108  *      Purpose: Free socket and private connection data
109  *
110  *************************************************************************/
111 static int sql_destroy_socket(SQLSOCK *sqlsocket, UNUSED SQL_CONFIG *config)
112 {
113         free(sqlsocket->conn);
114         sqlsocket->conn = NULL;
115         return 0;
116 }
117
118 /*************************************************************************
119  *
120  *      Function: sql_query
121  *
122  *      Purpose: Issue a non-SELECT query (ie: update/delete/insert) to
123  *               the database.
124  *
125  *************************************************************************/
126 static int sql_query(SQLSOCK *sqlsocket, SQL_CONFIG *config, char *querystr) {
127
128         rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
129         SQLRETURN rcode;
130
131         rcode = SQLAllocStmt(iodbc_sock->dbc_handle,
132                              &iodbc_sock->stmt_handle);
133         if (!SQL_SUCCEEDED(rcode)) {
134                 radlog(L_CONS|L_ERR, "sql_create_socket: SQLAllocStmt failed:  %s",
135                                 sql_error(sqlsocket, config));
136                 return -1;
137         }
138
139         if (config->sqltrace)
140                 radlog(L_DBG, "rlm_sql:  %s", querystr);
141         if (iodbc_sock->dbc_handle == NULL) {
142                 radlog(L_ERR, "sql_query:  Socket not connected");
143                 return -1;
144         }
145
146         rcode = SQLExecDirect(iodbc_sock->stmt_handle, querystr, SQL_NTS);
147         if (!SQL_SUCCEEDED(rcode)) {
148                 radlog(L_CONS|L_ERR, "sql_query: failed:  %s",
149                                 sql_error(sqlsocket, config));
150                 return -1;
151         }
152
153         return 0;
154 }
155
156
157 /*************************************************************************
158  *
159  *      Function: sql_select_query
160  *
161  *      Purpose: Issue a select query to the database
162  *
163  *************************************************************************/
164 static int sql_select_query(SQLSOCK *sqlsocket, SQL_CONFIG *config, char *querystr) {
165
166         int numfields = 0;
167         int i=0;
168         char **row=NULL;
169         SQLINTEGER len=0;
170         rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
171
172         if(sql_query(sqlsocket, config, querystr) < 0) {
173                 return -1;
174         }
175
176         numfields = sql_num_fields(sqlsocket, config);
177
178         row = (char **) rad_malloc(sizeof(char *) * (numfields+1));
179         memset(row, 0, (sizeof(char *) * (numfields)));
180         row[numfields] = NULL;
181
182         for(i=1; i<=numfields; i++) {
183                 SQLColAttributes(iodbc_sock->stmt_handle, ((SQLUSMALLINT) i), SQL_COLUMN_LENGTH,
184                                                                                 NULL, 0, NULL, &len);
185                 len++;
186
187                 /*
188                  * Allocate space for each column
189                  */
190                 row[i-1] = (SQLCHAR*)rad_malloc((int)len);
191
192                 /*
193                  * This makes me feel dirty, but, according to Microsoft, it works.
194                  * Any ODBC datatype can be converted to a 'char *' according to
195                  * the following:
196                  *
197                  * http://msdn.microsoft.com/library/psdk/dasdk/odap4o4z.htm
198                  */
199                 SQLBindCol(iodbc_sock->stmt_handle, i, SQL_C_CHAR, (SQLCHAR *)row[i-1], len, 0);
200         }
201
202         iodbc_sock->row = row;
203
204         return 0;
205 }
206
207
208 /*************************************************************************
209  *
210  *      Function: sql_store_result
211  *
212  *      Purpose: database specific store_result function. Returns a result
213  *               set for the query.
214  *
215  *************************************************************************/
216 static int sql_store_result(UNUSED SQLSOCK *sqlsocket, UNUSED SQL_CONFIG *config) {
217
218         return 0;
219 }
220
221
222 /*************************************************************************
223  *
224  *      Function: sql_num_fields
225  *
226  *      Purpose: database specific num_fields function. Returns number
227  *               of columns from query
228  *
229  *************************************************************************/
230 static int sql_num_fields(SQLSOCK *sqlsocket, UNUSED SQL_CONFIG *config) {
231
232         SQLSMALLINT count=0;
233         rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
234
235         SQLNumResultCols(iodbc_sock->stmt_handle, &count);
236
237         return (int)count;
238 }
239
240 /*************************************************************************
241  *
242  *      Function: sql_num_rows
243  *
244  *      Purpose: database specific num_rows. Returns number of rows in
245  *               query
246  *
247  *************************************************************************/
248 static int sql_num_rows(UNUSED SQLSOCK *sqlsocket, UNUSED SQL_CONFIG *config) {
249         /*
250          * I presume this function is used to determine the number of
251          * rows in a result set *before* fetching them.  I don't think
252          * this is possible in ODBC 2.x, but I'd be happy to be proven
253          * wrong.  If you know how to do this, email me at jeff@apex.net
254          */
255         return 0;
256 }
257
258
259 /*************************************************************************
260  *
261  *      Function: sql_fetch_row
262  *
263  *      Purpose: database specific fetch_row. Returns a SQL_ROW struct
264  *               with all the data for the query in 'sqlsocket->row'. Returns
265  *               0 on success, -1 on failure, SQL_DOWN if 'database is down'
266  *
267  *************************************************************************/
268 static int sql_fetch_row(SQLSOCK *sqlsocket, UNUSED SQL_CONFIG *config) {
269
270         SQLRETURN rc;
271         rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
272
273         sqlsocket->row = NULL;
274
275         if((rc = SQLFetch(iodbc_sock->stmt_handle)) == SQL_NO_DATA_FOUND) {
276                 return 0;
277         }
278         /* XXX Check rc for database down, if so, return SQL_DOWN */
279
280         sqlsocket->row = iodbc_sock->row;
281         return 0;
282 }
283
284
285
286 /*************************************************************************
287  *
288  *      Function: sql_free_result
289  *
290  *      Purpose: database specific free_result. Frees memory allocated
291  *               for a result set
292  *
293  *************************************************************************/
294 static int sql_free_result(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
295
296         int i=0;
297         rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
298
299         for(i=0; i<sql_num_fields(sqlsocket, config); i++) {
300                 free(iodbc_sock->row[i]);
301         }
302         free(iodbc_sock->row);
303         iodbc_sock->row=NULL;
304
305         SQLFreeStmt( iodbc_sock->stmt_handle, SQL_DROP );
306
307         return 0;
308 }
309
310
311 /*************************************************************************
312  *
313  *      Function: sql_error
314  *
315  *      Purpose: database specific error. Returns error associated with
316  *               connection
317  *
318  *************************************************************************/
319 static char *sql_error(SQLSOCK *sqlsocket, UNUSED SQL_CONFIG *config) {
320
321         SQLINTEGER errornum = 0;
322         SQLSMALLINT length = 0;
323         SQLCHAR state[256] = "";
324         static SQLCHAR error[256] = "";
325         rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
326
327         SQLError(iodbc_sock->env_handle, iodbc_sock->dbc_handle, iodbc_sock->stmt_handle,
328                 state, &errornum, error, 256, &length);
329         return error;
330 }
331
332
333 /*************************************************************************
334  *
335  *      Function: sql_close
336  *
337  *      Purpose: database specific close. Closes an open database
338  *               connection and cleans up any open handles.
339  *
340  *************************************************************************/
341 static int sql_close(SQLSOCK *sqlsocket, UNUSED SQL_CONFIG *config) {
342
343         rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
344
345         SQLFreeStmt(iodbc_sock->stmt_handle, SQL_DROP);
346         SQLDisconnect(iodbc_sock->dbc_handle);
347         SQLFreeConnect(iodbc_sock->dbc_handle);
348         SQLFreeEnv(iodbc_sock->env_handle);
349
350         iodbc_sock->stmt_handle = NULL;
351         iodbc_sock->dbc_handle = NULL;
352         iodbc_sock->env_handle = NULL;
353
354         return 0;
355 }
356
357
358 /*************************************************************************
359  *
360  *      Function: sql_finish_query
361  *
362  *      Purpose: End the query, such as freeing memory
363  *
364  *************************************************************************/
365 static int sql_finish_query(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
366
367         return sql_free_result(sqlsocket, config);
368 }
369
370
371
372 /*************************************************************************
373  *
374  *      Function: sql_finish_select_query
375  *
376  *      Purpose: End the select query, such as freeing memory or result
377  *
378  *************************************************************************/
379 static int sql_finish_select_query(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
380         return sql_free_result(sqlsocket, config);
381 }
382
383
384 /*************************************************************************
385  *
386  *      Function: sql_affected_rows
387  *
388  *      Purpose: Return the number of rows affected by the query (update,
389  *               or insert)
390  *
391  *************************************************************************/
392 static int sql_affected_rows(SQLSOCK *sqlsocket, UNUSED SQL_CONFIG *config) {
393
394         SQLINTEGER count;
395         rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
396
397         SQLRowCount(iodbc_sock->stmt_handle, &count);
398         return (int)count;
399 }
400
401 /* Exported to rlm_sql */
402 rlm_sql_module_t rlm_sql_iodbc = {
403         "rlm_sql_iodbc",
404         sql_init_socket,
405         sql_destroy_socket,
406         sql_query,
407         sql_select_query,
408         sql_store_result,
409         sql_num_fields,
410         sql_num_rows,
411         sql_fetch_row,
412         sql_free_result,
413         sql_error,
414         sql_close,
415         sql_finish_query,
416         sql_finish_select_query,
417         sql_affected_rows
418 };