Normalise conn field names in iodbc
[freeradius.git] / src / modules / rlm_sql / drivers / rlm_sql_db2 / rlm_sql_db2.c
1 /*
2  * sql_db2.c            IBM DB2 rlm_sql driver
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  Mike Machado <mike@innercite.com>
22  * Copyright 2000  Alan DeKok <aland@ox.org>
23  * Copyright 2001  Joerg Wendland <wendland@scan-plus.de>
24  */
25
26 /*
27  * Modification of rlm_sql_db2 to handle IBM DB2 UDB V7
28  * by Joerg Wendland <wendland@scan-plus.de>
29  */
30
31 RCSID("$Id$")
32
33 #include <freeradius-devel/radiusd.h>
34 #include <freeradius-devel/rad_assert.h>
35
36 #include <sys/stat.h>
37
38 #include <sql.h>
39 #include <sqlcli.h>
40 #include "rlm_sql.h"
41
42 typedef struct rlm_sql_conn {
43         SQLHANDLE dbc_handle;
44         SQLHANDLE env_handle;
45         SQLHANDLE stmt;
46 } rlm_sql_db2_conn_t;
47
48 static int _sql_socket_destructor(rlm_sql_db2_conn_t *conn)
49 {
50         DEBUG2("rlm_sql_db2: Socket destructor called, closing socket");
51
52         if (conn->dbc_handle) {
53                 SQLDisconnect(conn->dbc_handle);
54                 SQLFreeHandle(SQL_HANDLE_DBC, conn->dbc_handle);
55         }
56
57         if (conn->env_handle) SQLFreeHandle(SQL_HANDLE_ENV, conn->env_handle);
58
59         return RLM_SQL_OK;
60 }
61
62 static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
63 {
64         SQLRETURN retval;
65         rlm_sql_db2_conn_t *conn;
66
67         MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_db2_conn_t));
68         talloc_set_destructor(conn, _sql_socket_destructor);
69
70         /* Allocate handles */
71         SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &(conn->env_handle));
72         SQLAllocHandle(SQL_HANDLE_DBC, conn->env_handle, &(conn->dbc_handle));
73
74         /*
75          *      The db2 API doesn't qualify arguments as const even when they should be.
76          */
77         {
78                 SQLCHAR *server, *login, *password;
79
80                 memcpy(&server, &config->sql_server, sizeof(server));
81                 memcpy(&login, &config->sql_login, sizeof(login));
82                 memcpy(&password, &config->sql_password, sizeof(password));
83
84                 retval = SQLConnect(conn->dbc_handle,
85                                     server, SQL_NTS,
86                                     login,  SQL_NTS,
87                                     password, SQL_NTS);
88         }
89
90         if (retval != SQL_SUCCESS) {
91                 ERROR("could not connect to DB2 server %s", config->sql_server);
92
93                 return RLM_SQL_ERROR;
94         }
95
96         return RLM_SQL_OK;
97 }
98
99 static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config, char const *query)
100 {
101         SQLRETURN retval;
102         rlm_sql_db2_conn_t *conn;
103
104         conn = handle->conn;
105
106         /* allocate handle for statement */
107         SQLAllocHandle(SQL_HANDLE_STMT, conn->dbc_handle, &(conn->stmt));
108
109         /* execute query */
110         {
111                 SQLCHAR *db2_query;
112                 memcpy(&db2_query, &query, sizeof(query));
113
114                 retval = SQLExecDirect(conn->stmt, db2_query, SQL_NTS);
115                 if(retval != SQL_SUCCESS) {
116                         /* XXX Check if retval means we should return RLM_SQL_RECONNECT */
117                         ERROR("Could not execute statement \"%s\"", query);
118                         return RLM_SQL_ERROR;
119                 }
120         }
121
122         return RLM_SQL_OK;
123 }
124
125 static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query)
126 {
127         return sql_query(handle, config, query);
128 }
129
130 static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
131 {
132         SQLSMALLINT c;
133         rlm_sql_db2_conn_t *conn;
134
135         conn = handle->conn;
136         SQLNumResultCols(conn->stmt, &c);
137         return c;
138 }
139
140 static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
141 {
142         rlm_sql_db2_conn_t *conn = handle->conn;
143
144         SQLSMALLINT     fields, len, i;
145
146         char const      **names;
147         char            field[128];
148
149         SQLNumResultCols(conn->stmt, &fields);
150         if (fields == 0) return RLM_SQL_ERROR;
151
152         MEM(names = talloc_array(handle, char const *, fields));
153
154         for (i = 0; i < fields; i++) {
155                 char *p;
156
157                 switch (SQLColAttribute(conn->stmt, i, SQL_DESC_BASE_COLUMN_NAME,
158                                         field, sizeof(field), &len, NULL)) {
159                 case SQL_INVALID_HANDLE:
160                 case SQL_ERROR:
161                         ERROR("Failed retrieving field name at index %i", i);
162                         talloc_free(names);
163                         return RLM_SQL_ERROR;
164
165                 default:
166                         break;
167                 }
168
169                 MEM(p = talloc_array(names, char, (size_t)len + 1));
170                 strlcpy(p, field, (size_t)len + 1);
171                 names[i] = p;
172         }
173         *out = names;
174
175         return RLM_SQL_OK;
176 }
177
178 static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
179 {
180         int c, i;
181         SQLINTEGER len, slen;
182         rlm_sql_row_t retval;
183         rlm_sql_db2_conn_t *conn;
184
185         conn = handle->conn;
186
187         c = sql_num_fields(handle, config);
188         retval = (rlm_sql_row_t)rad_malloc(c*sizeof(char*)+1);
189         memset(retval, 0, c*sizeof(char*)+1);
190
191         /* advance cursor */
192         if(SQLFetch(conn->stmt) == SQL_NO_DATA_FOUND) {
193                 handle->row = NULL;
194                 goto error;
195         }
196
197         for(i = 0; i < c; i++) {
198                 /* get column length */
199                 SQLColAttribute(conn->stmt, i+1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &len);
200
201                 retval[i] = rad_malloc(len+1);
202
203                 /* get the actual column */
204                 SQLGetData(conn->stmt, i + 1, SQL_C_CHAR, retval[i], len+1, &slen);
205                 if(slen == SQL_NULL_DATA) {
206                         retval[i][0] = '\0';
207                 }
208         }
209
210         handle->row = retval;
211         return RLM_SQL_OK;
212
213 error:
214         for(i = 0; i < c; i++) {
215                 free(retval[i]);
216         }
217         free(retval);
218
219         return RLM_SQL_ERROR;
220 }
221
222 static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
223 {
224         rlm_sql_db2_conn_t *conn;
225         conn = handle->conn;
226         SQLFreeHandle(SQL_HANDLE_STMT, conn->stmt);
227
228         return RLM_SQL_OK;
229 }
230
231 /** Retrieves any errors associated with the connection handle
232  *
233  * @note Caller will free any memory allocated in ctx.
234  *
235  * @param ctx to allocate temporary error buffers in.
236  * @param out Array of sql_log_entrys to fill.
237  * @param outlen Length of out array.
238  * @param handle rlm_sql connection handle.
239  * @param config rlm_sql config.
240  * @return number of errors written to the sql_log_entry array.
241  */
242 static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
243                         rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
244 {
245         char                    state[6];
246         char                    errbuff[1024];
247         SQLINTEGER              err;
248         SQLSMALLINT             rl;
249         rlm_sql_db2_conn_t      *conn = handle->conn;
250
251         rad_assert(conn);
252         rad_assert(outlen > 0);
253
254         errbuff[0] = '\0';
255         SQLGetDiagRec(SQL_HANDLE_STMT, conn->stmt, 1, (SQLCHAR *) state, &err,
256                       (SQLCHAR *) errbuff, sizeof(errbuff), &rl);
257         if (errbuff[0] == '\0') return 0;
258
259         out[0].type = L_ERR;
260         out[0].msg = talloc_asprintf(ctx, "%s: %s", state, errbuff);
261
262         return 1;
263 }
264
265 static sql_rcode_t sql_finish_query(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
266 {
267         return RLM_SQL_OK;
268 }
269
270 static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
271 {
272         return sql_finish_query(handle, config);
273 }
274
275 static int sql_affected_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
276 {
277         SQLINTEGER c;
278         rlm_sql_db2_conn_t *conn = handle->conn;
279
280         SQLRowCount(conn->stmt, &c);
281
282         return c;
283 }
284
285 /* Exported to rlm_sql */
286 extern rlm_sql_module_t rlm_sql_db2;
287 rlm_sql_module_t rlm_sql_db2 = {
288         .name                           = "rlm_sql_db2",
289         .sql_socket_init                = sql_socket_init,
290         .sql_query                      = sql_query,
291         .sql_select_query               = sql_select_query,
292         .sql_num_fields                 = sql_num_fields,
293         .sql_affected_rows              = sql_affected_rows,
294         .sql_fields                     = sql_fields,
295         .sql_fetch_row                  = sql_fetch_row,
296         .sql_free_result                = sql_free_result,
297         .sql_error                      = sql_error,
298         .sql_finish_query               = sql_finish_query,
299         .sql_finish_select_query        = sql_finish_select_query
300 };