The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_sql_db2.c
Go to the documentation of this file.
1/*
2 * sql_db2.c IBM DB2 rlm_sql driver
3 *
4 * Version: $Id: 36e29ce5368cec04b81fc4aac182d602e198d389 $
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@freeradius.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 */
30RCSID("$Id: 36e29ce5368cec04b81fc4aac182d602e198d389 $")
31
32#define LOG_PREFIX "sql - db2"
33
34#include <freeradius-devel/server/base.h>
35#include <freeradius-devel/util/debug.h>
36
37#include <sys/stat.h>
38
39#include <sqlcli1.h>
40#include <sqlstate.h>
41#include "rlm_sql.h"
42#include "rlm_sql_trunk.h"
43
44typedef struct {
45 SQLHANDLE dbc_handle;
46 SQLHANDLE env_handle;
47 SQLHANDLE stmt;
49
50static void _sql_connection_close(UNUSED fr_event_list_t *el, void *h, UNUSED void *uctx)
51{
52 rlm_sql_db2_conn_t *conn = talloc_get_type_abort(h, rlm_sql_db2_conn_t);
53
54 DEBUG2("Socket destructor called, closing socket");
55
56 if (conn->dbc_handle) {
57 SQLDisconnect(conn->dbc_handle);
58 SQLFreeHandle(SQL_HANDLE_DBC, conn->dbc_handle);
59 }
60
61 if (conn->env_handle) SQLFreeHandle(SQL_HANDLE_ENV, conn->env_handle);
62
63 talloc_free(h);
64}
65
66CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
67static connection_state_t _sql_connection_init(void **h, connection_t *conn, void *uctx)
68{
71 rlm_sql_config_t const *config = &sql->config;
72 uint32_t timeout_ms = fr_time_delta_to_msec(config->trunk_conf.conn_conf->connection_timeout);
73 SQLRETURN ret;
74
75 MEM(c = talloc_zero(conn, rlm_sql_db2_conn_t));
76
77 /* Allocate handles */
78 SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &(c->env_handle));
79 SQLAllocHandle(SQL_HANDLE_DBC, c->env_handle, &(c->dbc_handle));
80
81 /* Set the connection timeout */
82 SQLSetConnectAttr(c->dbc_handle, SQL_ATTR_LOGIN_TIMEOUT, &timeout_ms, SQL_IS_UINTEGER);
83
84 /*
85 * We probably want to use SQLDriverConnect, which connects
86 * to a remote server.
87 *
88 * http://www.ibm.com/support/knowledgecenter/SSEPGG_10.5.0/com.ibm.db2.luw.apdv.cli.doc/doc/r0000584.html
89 * http://stackoverflow.com/questions/27167070/connection-string-to-a-remote-db2-db-in-another-server
90 *
91 * And probably synthesise the retarded connection string ourselves,
92 * probably via config file expansions:
93 *
94 * Driver={IBM DB2 ODBC Driver};Database=testDb;Hostname=remoteHostName.com;UID=username;PWD=mypasswd;PORT=50000
95 */
96 ret = SQLConnect(c->dbc_handle,
97 UNCONST(SQLCHAR *, config->sql_server), SQL_NTS,
98 UNCONST(SQLCHAR *, config->sql_login), SQL_NTS,
99 UNCONST(SQLCHAR *, config->sql_password), SQL_NTS);
100 if (ret != SQL_SUCCESS) {
101 ERROR("could not connect to DB2 server %s", config->sql_server);
102
104 }
105
106 *h = c;
108}
109
111
113
114CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
116 connection_t *conn, UNUSED void *uctx)
117{
118 rlm_sql_db2_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_db2_conn_t);
119 trunk_request_t *treq;
120 request_t *request;
121 fr_sql_query_t *query_ctx;
122 SQLRETURN ret;
123 SQLCHAR *db2_query;
124
125 if (trunk_connection_pop_request(&treq, tconn) != 0) return;
126 if (!treq) return;
127
128 query_ctx = talloc_get_type_abort(treq->preq, fr_sql_query_t);
129 request = query_ctx->request;
130 query_ctx->tconn = tconn;
131
132 ROPTIONAL(RDEBUG2, DEBUG2, "Executing query: %s", query_ctx->query_str);
133
134 /* allocate handle for statement */
135 SQLAllocHandle(SQL_HANDLE_STMT, sql_conn->dbc_handle, &(sql_conn->stmt));
136
137 /* execute query */
138 memcpy(&db2_query, &query_ctx->query_str, sizeof(query_ctx->query_str));
139
140 ret = SQLExecDirect(sql_conn->stmt, db2_query, SQL_NTS);
141 if (ret != SQL_SUCCESS) {
142 SQLCHAR state[6];
143 SQLSMALLINT len;
144
145 SQLGetDiagField(SQL_HANDLE_STMT, sql_conn->dbc_handle, 1, SQL_DIAG_SQLSTATE, state, sizeof(state), &len);
146
147 if (strncmp((char *)state, SQL_CONSTR_INDEX_UNIQUE, 5)) {
148 query_ctx->rcode = RLM_SQL_ALT_QUERY;
149 goto finish;
150 }
151
152 /* XXX Check if ret means we should return RLM_SQL_RECONNECT */
153 ERROR("Could not execute statement \"%s\"", query_ctx->query_str);
154 query_ctx->rcode = RLM_SQL_ERROR;
156 return;
157 }
158
159 query_ctx->rcode = RLM_SQL_OK;
160finish:
161 query_ctx->status = SQL_QUERY_RETURNED;
163 if (request) unlang_interpret_mark_runnable(request);
164}
165
166static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
167{
168 rlm_sql_db2_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_db2_conn_t);
169
170 SQLSMALLINT fields, len, i;
171
172 char const **names;
173 char field[128];
174
175 SQLNumResultCols(conn->stmt, &fields);
176 if (fields == 0) return RLM_SQL_ERROR;
177
178 MEM(names = talloc_array(query_ctx, char const *, fields));
179
180 for (i = 0; i < fields; i++) {
181 char *p;
182
183 switch (SQLColAttribute(conn->stmt, i, SQL_DESC_BASE_COLUMN_NAME,
184 field, sizeof(field), &len, NULL)) {
185 case SQL_INVALID_HANDLE:
186 case SQL_ERROR:
187 ERROR("Failed retrieving field name at index %i", i);
189 return RLM_SQL_ERROR;
190
191 default:
192 break;
193 }
194
195 MEM(p = talloc_array(names, char, (size_t)len + 1));
196 strlcpy(p, field, (size_t)len + 1);
197 names[i] = p;
198 }
199 *out = names;
200
201 return RLM_SQL_OK;
202}
203
204static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
205{
206 fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
207 int i;
208 SQLINTEGER len, slen;
209 SQLSMALLINT c;
210 rlm_sql_row_t row;
211 rlm_sql_db2_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_db2_conn_t);
212
213 TALLOC_FREE(query_ctx->row);
214
215 SQLNumResultCols(conn->stmt, &c);
216
217 /* advance cursor */
218 if (SQLFetch(conn->stmt) == SQL_NO_DATA_FOUND) {
219 query_ctx->rcode = RLM_SQL_NO_MORE_ROWS;
221 }
222
223 MEM(row = (rlm_sql_row_t)talloc_zero_array(query_ctx, char *, c + 1));
224 for (i = 0; i < c; i++) {
225 /* get column length */
226 SQLColAttribute(conn->stmt, i + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &len);
227
228 MEM(row[i] = talloc_array(row, char, len + 1));
229
230 /* get the actual column */
231 SQLGetData(conn->stmt, i + 1, SQL_C_CHAR, row[i], len + 1, &slen);
232 if (slen == SQL_NULL_DATA) row[i][0] = '\0';
233 }
234
235 query_ctx->row = row;
236
237 query_ctx->rcode = RLM_SQL_OK;
239}
240
242{
243 rlm_sql_db2_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_db2_conn_t);
244
245 TALLOC_FREE(query_ctx->row);
246 SQLFreeHandle(SQL_HANDLE_STMT, conn->stmt);
247
248 return RLM_SQL_OK;
249}
250
251/** Retrieves any errors associated with the query context
252 *
253 * @note Caller will free any memory allocated in ctx.
254 *
255 * @param ctx to allocate temporary error buffers in.
256 * @param out Array of sql_log_entrys to fill.
257 * @param outlen Length of out array.
258 * @param query_ctx Query context to retrieve error for.
259 * @return number of errors written to the #sql_log_entry_t array.
260 */
261static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], NDEBUG_UNUSED size_t outlen,
262 fr_sql_query_t *query_ctx)
263{
264 char state[6];
265 char errbuff[1024];
266 SQLINTEGER err;
267 SQLSMALLINT rl;
268 rlm_sql_db2_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_db2_conn_t);
269
270 fr_assert(conn);
271 fr_assert(outlen > 0);
272
273 errbuff[0] = '\0';
274 SQLGetDiagRec(SQL_HANDLE_STMT, conn->stmt, 1, (SQLCHAR *) state, &err,
275 (SQLCHAR *) errbuff, sizeof(errbuff), &rl);
276 if (errbuff[0] == '\0') return 0;
277
278 out[0].type = L_ERR;
279 out[0].msg = talloc_typed_asprintf(ctx, "%s: %s", state, errbuff);
280
281 return 1;
282}
283
288
290{
291 SQLINTEGER c;
292 rlm_sql_db2_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_db2_conn_t);
293
294 SQLRowCount(conn->stmt, &c);
295
296 return c;
297}
298
299static void sql_request_fail(request_t *request, void *preq, UNUSED void *rctx,
300 UNUSED trunk_request_state_t state, UNUSED void *uctx)
301{
302 fr_sql_query_t *query_ctx = talloc_get_type_abort(preq, fr_sql_query_t);
303
304 query_ctx->treq = NULL;
305 if (query_ctx->rcode == RLM_SQL_OK) query_ctx->rcode = RLM_SQL_ERROR;
306 if (request) unlang_interpret_mark_runnable(request);
307}
308
309/* Exported to rlm_sql */
312 .common = {
313 .magic = MODULE_MAGIC_INIT,
314 .name = "sql_db2",
315 },
317 .sql_query_resume = sql_query_resume,
318 .sql_select_query_resume = sql_query_resume,
319 .sql_affected_rows = sql_affected_rows,
320 .sql_fields = sql_fields,
321 .sql_fetch_row = sql_fetch_row,
322 .sql_free_result = sql_free_result,
323 .sql_error = sql_error,
324 .sql_finish_query = sql_finish_query,
325 .sql_finish_select_query = sql_finish_query,
326 .trunk_io_funcs = {
327 .connection_alloc = sql_trunk_connection_alloc,
328 .request_mux = sql_trunk_request_mux,
329 .request_fail = sql_request_fail,
330 }
331};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:167
#define RCSID(id)
Definition build.h:483
#define NDEBUG_UNUSED
Definition build.h:326
#define CC_NO_UBSAN(_sanitize)
Definition build.h:426
#define UNUSED
Definition build.h:315
connection_state_t
Definition connection.h:45
@ CONNECTION_STATE_FAILED
Connection has failed.
Definition connection.h:54
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
Definition connection.h:52
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:41
static fr_slen_t err
Definition dict.h:824
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:1359
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition log.h:528
talloc_free(reap)
Stores all information relating to an event list.
Definition event.c:411
@ L_ERR
Error message.
Definition log.h:56
unsigned int uint32_t
static const conf_parser_t config[]
Definition base.c:183
#define fr_assert(_expr)
Definition rad_assert.h:38
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define DEBUG2(fmt,...)
Definition radclient.h:43
#define RETURN_MODULE_OK
Definition rcode.h:57
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
Prototypes and functions for the SQL module.
fr_sql_query_status_t status
Status of the query.
Definition rlm_sql.h:138
trunk_connection_t * tconn
Trunk connection this query is being run on.
Definition rlm_sql.h:134
char const * query_str
Query string to run.
Definition rlm_sql.h:136
request_t * request
Request this query relates to.
Definition rlm_sql.h:132
sql_rcode_t
Action to take at end of an SQL query.
Definition rlm_sql.h:44
@ RLM_SQL_ALT_QUERY
Key constraint violation, use an alternative query.
Definition rlm_sql.h:49
@ RLM_SQL_ERROR
General connection/server error.
Definition rlm_sql.h:46
@ RLM_SQL_OK
Success.
Definition rlm_sql.h:47
@ RLM_SQL_NO_MORE_ROWS
No more rows available.
Definition rlm_sql.h:50
#define RLM_SQL_RCODE_FLAGS_ALT_QUERY
Can distinguish between other errors and those resulting from a unique key violation.
Definition rlm_sql.h:164
char ** rlm_sql_row_t
Definition rlm_sql.h:59
rlm_sql_row_t row
Row data from the last query.
Definition rlm_sql.h:140
sql_rcode_t rcode
Result code.
Definition rlm_sql.h:139
trunk_request_t * treq
Trunk request for this query.
Definition rlm_sql.h:135
@ SQL_QUERY_RETURNED
Query has executed.
Definition rlm_sql.h:124
Definition rlm_sql.h:61
rlm_sql_driver_t rlm_sql_db2
static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], NDEBUG_UNUSED size_t outlen, fr_sql_query_t *query_ctx)
Retrieves any errors associated with the query context.
static connection_state_t _sql_connection_init(void **h, connection_t *conn, void *uctx)
Definition rlm_sql_db2.c:67
SQL_TRUNK_CONNECTION_ALLOC static SQL_QUERY_RESUME void sql_trunk_request_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
static void _sql_connection_close(UNUSED fr_event_list_t *el, void *h, UNUSED void *uctx)
Definition rlm_sql_db2.c:50
static sql_rcode_t sql_finish_query(UNUSED fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
SQLHANDLE dbc_handle
Definition rlm_sql_db2.c:45
static sql_rcode_t sql_free_result(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
static int sql_affected_rows(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
static void sql_request_fail(request_t *request, void *preq, UNUSED void *rctx, UNUSED trunk_request_state_t state, UNUSED void *uctx)
SQLHANDLE env_handle
Definition rlm_sql_db2.c:46
Macros to reduce boilerplate in trunk SQL drivers.
#define SQL_QUERY_RESUME
#define SQL_TRUNK_CONNECTION_ALLOC
Allocate an SQL trunk connection.
module_flags_t flags
Flags that control how a module starts up and how a module is called.
Definition module.h:227
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
module_t common
Common fields for all loadable modules.
Definition rlm_sql.h:194
rlm_sql_config_t config
Definition rlm_sql.h:221
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition talloc.c:492
#define talloc_get_type_abort_const
Definition talloc.h:282
static const char * names[8]
Definition time.c:621
static int64_t fr_time_delta_to_msec(fr_time_delta_t delta)
Definition time.h:637
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
Definition trunk.c:2132
int trunk_connection_pop_request(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a request off a connection's pending queue.
Definition trunk.c:3883
void trunk_request_signal_reapable(trunk_request_t *treq)
Signal that the request was written to a connection successfully, but no response is expected.
Definition trunk.c:2072
Associates request queues with a connection.
Definition trunk.c:134
Wraps a normal request.
Definition trunk.c:100
trunk_request_state_t
Used for sanity checks and to simplify freeing.
Definition trunk.h:161
static fr_event_list_t * el
static size_t char ** out
Definition value.h:997