f9c6e9ec0fed284b3ea6d0c6c9b1c1954294b6c1
[freeradius.git] / src / modules / rlm_sql / drivers / rlm_sql_freetds / rlm_sql_freetds.c
1 /*
2  *  sql_freetds.c
3  *  freeradius
4  *
5  * Version:     $Id$
6  *
7  *   This program is free software; you can redistribute it and/or modify
8  *   it under the terms of the GNU General Public License version 2 only, as published by
9  *   the Free Software Foundation.
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 version 2
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * Copyright 2009 Gabriel Blanchard gabe@teksavvy.com
21  */
22
23 #include <freeradius-devel/ident.h>
24 RCSID("$Id$")
25
26 #include <freeradius-devel/radiusd.h>
27
28 #include <sybdb.h>
29 #include <sybfront.h>
30
31 #include <sys/time.h>
32 #include <sys/stat.h>
33
34 #include "rlm_sql.h"
35 static int query_timeout_handler(void *dbproc);
36 static int err_handler(UNUSED DBPROCESS *dbproc, UNUSED int severity, UNUSED int dberr, UNUSED int oserr, char *dberrstr, char *oserrstr);
37
38 // As defined in tds.h
39 #define TDS_INT_CONTINUE 1
40 #define TDS_INT_CANCEL 2
41 #define TDS_INT_TIMEOUT 3
42
43 typedef struct rlm_sql_freetds_sock {
44         DBPROCESS *dbproc;
45 } rlm_sql_freetds_sock;
46
47 /*
48  * FreeTDS calls this handler when dbsqlexec() or dbsqlok() blocks every seconds
49  * This ensures that FreeTDS doesn't stay stuck on the same query for ever.
50  */
51 static int query_timeout_handler(void *dbproc) {
52         return TDS_INT_CONTINUE;
53 }
54
55 static int err_handler(UNUSED DBPROCESS *dbproc, UNUSED int severity, UNUSED int dberr, UNUSED int oserr, char *dberrstr, char *oserrstr)
56 {       
57                 radlog(L_ERR, "rlm_sql_freetds: FreeTDS error: %s\n", dberrstr);
58                 radlog(L_ERR, "rlm_sql_freetds: OS error: %s\n", oserrstr);
59 }
60         
61 /*************************************************************************
62  *
63  *      Function: sql_create_socket
64  *
65  *      Purpose: Establish connection to the db
66  *
67  *************************************************************************/
68 static int sql_init_socket(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
69 {
70         LOGINREC *login;
71         rlm_sql_freetds_sock *freetds_sock;
72         
73         if (!handle->conn) {
74                 handle->conn = (rlm_sql_freetds_sock *)rad_malloc(sizeof(struct rlm_sql_freetds_sock));
75                 if (!handle->conn) {
76                         return -1;
77                 }
78         }
79         
80         if (dbinit() == FAIL) {
81                 radlog(L_ERR, "rlm_sql_freetds: Unable to init FreeTDS");
82                 return -1;              
83         }
84         
85         dbsetversion(DBVERSION_80);
86         dberrhandle(err_handler);
87         
88         // Timeout so that FreeTDS doesn't wait for ever.
89         dbsetlogintime((unsigned long)config->query_timeout);
90         dbsettime((unsigned long)config->query_timeout);
91         
92         freetds_sock = handle->conn;
93         memset(freetds_sock, 0, sizeof(*freetds_sock));
94         
95         DEBUG("rlm_sql_freetds (%s): Starting connect to FreeTDS/MSSQL server", config->xlat_name);
96         
97         if (!(login = dblogin())) {
98                 radlog(L_ERR, "rlm_sql_freetds (%s): Unable to allocate login record", config->xlat_name);
99                 return -1;
100         }
101         
102         DBSETLUSER(login, config->sql_login);
103         DBSETLPWD(login, config->sql_password);
104         
105         if ((freetds_sock->dbproc = dbopen(login, config->sql_server)) == FAIL) {
106                 radlog(L_ERR, "rlm_sql_freetds (%s): Unable to connect to FreeTDS/MSSQL server %s@%s", 
107                            config->xlat_name, config->sql_login, config->sql_server);
108                 dbloginfree(login);
109                 return -1;
110         }
111         
112         dbloginfree(login);
113         
114         if ((dbuse(freetds_sock->dbproc, config->sql_db)) == FAIL) {
115                 radlog(L_ERR, "rlm_sql_freetds (%s): Unable to select database on FreeTDS/MSSQL server %s@%s:%s", 
116                            config->xlat_name, config->sql_login, config->sql_server, config->sql_db);
117                 return -1;
118         }
119         
120         /* I know this may look strange, but it sets a pointer to
121          the freetds_sock struct so that it can be used within the
122          query_timeout_handler function to be able to timeout properly */
123         dbsetinterrupt(freetds_sock->dbproc, query_timeout_handler, query_timeout_handler);
124         dbsetuserdata(freetds_sock->dbproc, (BYTE *)freetds_sock);
125         
126         return 0;
127 }
128
129
130 /*************************************************************************
131  *
132  *      Function: sql_destroy_socket
133  *
134  *      Purpose: Free socket and any private connection data
135  *
136  *************************************************************************/
137 static int sql_destroy_socket(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
138 {
139         free(handle->conn);
140         handle->conn = NULL;
141         
142         return 0;
143 }
144
145
146 /*************************************************************************
147  *
148  *      Function: sql_query
149  *
150  *      Purpose: Issue a query to the database
151  *
152  *************************************************************************/
153 static int sql_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char *querystr)
154 {
155         rlm_sql_freetds_sock *freetds_sock = handle->conn;
156         
157         if (freetds_sock->dbproc == NULL || DBDEAD(freetds_sock->dbproc)) {
158                 radlog(L_ERR, "rlm_sql_freetds (%s): Socket not connected", config->xlat_name);
159                 return SQL_DOWN;
160         }
161         
162         if ((dbcmd(freetds_sock->dbproc, querystr)) == FAIL) {
163                 radlog(L_ERR, "rlm_sql_freetds (%s): Unable to allocate SQL query", config->xlat_name);
164                 return -1;
165         }
166         
167         if ((dbsqlexec(freetds_sock->dbproc)) == FAIL) {
168                 radlog(L_ERR, "rlm_sql_freetds (%s): SQL query failed", config->xlat_name);
169                 return -1;
170         }
171         
172         return 0;
173 }
174
175
176 /*************************************************************************
177  *
178  *      Function: sql_select_query
179  *
180  *      Purpose: Issue a select query to the database
181  *
182  *************************************************************************/
183 static int sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char *querystr)
184 {       
185         radlog(L_ERR, "rlm_sql_freetds sql_select_query(): unsupported");
186         return -1;
187 }
188
189
190 /*************************************************************************
191  *
192  *      Function: sql_store_result
193  *
194  *      Purpose: database specific store_result function. Returns a result
195  *               set for the query.
196  *
197  *************************************************************************/
198 static int sql_store_result(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
199 {
200         radlog(L_ERR, "rlm_sql_freetds sql_store_result(): unsupported");
201         return -1;
202 }
203
204
205 /*************************************************************************
206  *
207  *      Function: sql_num_fields
208  *
209  *      Purpose: database specific num_fields function. Returns number
210  *               of columns from query
211  *
212  *************************************************************************/
213 static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
214 {
215         rlm_sql_freetds_sock *freetds_sock = handle->conn;
216         
217         return dbnumcols(freetds_sock->dbproc);
218 }
219
220
221 /*************************************************************************
222  *
223  *      Function: sql_num_rows
224  *
225  *      Purpose: database specific num_rows. Returns number of rows in
226  *               query
227  *
228  *************************************************************************/
229 static int sql_num_rows(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
230 {
231         
232         return 0;
233 }
234
235 /*************************************************************************
236  *
237  *      Function: sql_fetch_row
238  *
239  *      Purpose: database specific fetch_row. Returns a rlm_sql_row_t struct
240  *               with all the data for the query in 'handle->row'. Returns
241  *               0 on success, -1 on failure, SQL_DOWN if database is down.
242  *
243  *************************************************************************/
244 static int sql_fetch_row(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
245 {
246         return 0;
247 }
248
249
250 /*************************************************************************
251  *
252  *      Function: sql_free_result
253  *
254  *      Purpose: database specific free_result. Frees memory allocated
255  *               for a result set
256  *
257  *************************************************************************/
258 static int sql_free_result(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
259 {
260
261         return 0;
262 }
263
264
265 /*************************************************************************
266  *
267  *      Function: sql_error
268  *
269  *      Purpose: database specific error. Returns error associated with
270  *               connection
271  *
272  *************************************************************************/
273 static const char *sql_error(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
274 {
275         return NULL;
276 }
277
278
279 /*************************************************************************
280  *
281  *      Function: sql_close
282  *
283  *      Purpose: database specific close. Closes an open database
284  *               connection
285  *
286  *************************************************************************/
287 static int sql_close(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
288 {
289         rlm_sql_freetds_sock *freetds_sock = handle->conn;
290         
291         if (freetds_sock && freetds_sock->dbproc){
292                 dbclose(freetds_sock->dbproc);
293                 freetds_sock->dbproc = NULL;
294         }
295         
296         return 0;
297 }
298
299
300 /*************************************************************************
301  *
302  *      Function: sql_finish_query
303  *
304  *      Purpose: End the query, such as freeing memory
305  *
306  *************************************************************************/
307 static int sql_finish_query(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
308 {
309
310         return 0;
311 }
312
313
314 /*************************************************************************
315  *
316  *      Function: sql_finish_select_query
317  *
318  *      Purpose: End the select query, such as freeing memory or result
319  *
320  *************************************************************************/
321 static int sql_finish_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
322 {
323         return sql_finish_query(handle, config);
324 }
325
326
327 /*************************************************************************
328  *
329  *      Function: sql_affected_rows
330  *
331  *      Purpose: End the select query, such as freeing memory or result
332  *
333  *************************************************************************/
334 static int sql_affected_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
335 {
336         rlm_sql_freetds_sock *freetds_sock = handle->conn;
337         
338         return dbcount(freetds_sock->dbproc);
339 }
340
341
342 /* Exported to rlm_sql */
343 rlm_sql_module_t rlm_sql_freetds = {
344         "rlm_sql_freetds",
345         sql_init_socket,
346         sql_destroy_socket,
347         sql_query,
348         sql_select_query,
349         sql_store_result,
350         sql_num_fields,
351         sql_num_rows,
352         sql_fetch_row,
353         sql_free_result,
354         sql_error,
355         sql_close,
356         sql_finish_query,
357         sql_finish_select_query,
358         sql_affected_rows
359 };