The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_sql_oracle.c
Go to the documentation of this file.
1/*
2 * sql_oracle.c Oracle (OCI) routines for rlm_sql
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 *
18 * @copyright 2019 Robert Biktimirov (pobept@gmail.com)
19 * @copyright 2000,2006 The FreeRADIUS server project
20 * @copyright 2000 David Kerry (davidk@snti.com)
21 */
22
23RCSID("$Id: d157225b289f1663a7b8789b9e94d794c69dd40a $")
24
25#define LOG_PREFIX "sql - oracle"
26
27#include <freeradius-devel/server/base.h>
28#include <freeradius-devel/util/debug.h>
29
30#include <sys/stat.h>
31
32/*
33 * There are typos in the Oracle Instaclient where the definition controlling prototype
34 * format is _STDC_ (not __STDC__).
35 *
36 * There are still cases where the oracle headers do not declare ANSI C function types
37 * but this at least cuts down the errors.
38 *
39 * -Wno-strict-prototypes does the rest.
40 */
41DIAG_OFF(unused-macros)
42#if defined(__STDC__) && __STDC__
43# define _STDC_
44#endif
45
46#include <oci.h>
47DIAG_ON(unused-macros)
48
49#include "rlm_sql.h"
50#include "rlm_sql_trunk.h"
51
52typedef struct {
53 OCIEnv *env; //!< Environment handle
54 uint32_t stmt_cache_size; //!< Statement cache size
56
57typedef struct {
58 OCIStmt *query; //!< Query handle
59 OCIError *error; //!< Error handle
60 OCIServer *srv; //!< Server handle
61 OCISvcCtx *ctx; //!< Service handle
62 OCISession *sess; //!< Session handle
63 sb2 *ind; //!< Indicators regarding contents of the results row.
64 rlm_sql_row_t row; //!< Results row
65 int col_count; //!< Number of columns associated with the result set
66 connection_t *conn; //!< Generic connection structure for this connection.
67 rlm_sql_config_t const *config; //!< SQL instance configuration.
68 fr_sql_query_t *query_ctx; //!< Current request running on the connection.
69 fr_event_timer_t const *read_ev; //!< Timer event for polling reading this connection
70 fr_event_timer_t const *write_ev; //!< Timer event for polling writing this connection
71 uint select_interval; //!< How frequently this connection gets polled for select queries.
72 uint query_interval; //!< How frequently this connection gets polled for other queries.
73 uint poll_count; //!< How many polls have been done for the current query.
75
76static const conf_parser_t driver_config[] = {
77 { FR_CONF_OFFSET("stmt_cache_size", rlm_sql_oracle_t, stmt_cache_size), .dflt = "32" },
79};
80
81#define MAX_DATASTR_LEN 64
82
83/** Write the last Oracle error out to a buffer
84 *
85 * @param out Where to write the error (should be at least 512 bytes).
86 * @param outlen The length of the error buffer.
87 * @param conn Oracle connection.
88 * @return
89 * - Oracle error code on success.
90 * - -1 if there was no error.
91 */
92static int sql_snprint_error(char *out, size_t outlen, rlm_sql_oracle_conn_t *conn)
93{
94 sb4 errcode = 0;
95
96 fr_assert(conn);
97
98 out[0] = '\0';
99
100 OCIErrorGet((dvoid *) conn->error, 1, (OraText *) NULL, &errcode, (OraText *) out,
101 outlen, OCI_HTYPE_ERROR);
102 if (!errcode) return -1;
103
104 return errcode;
105}
106
107/** Retrieves any errors associated with the query context
108 *
109 * @note Caller will free any memory allocated in ctx.
110 *
111 * @param ctx to allocate temporary error buffers in.
112 * @param out Array of sql_log_entrys to fill.
113 * @param outlen Length of out array.
114 * @param query_ctx Query context to retrieve error for.
115 * @return number of errors written to the #sql_log_entry_t array.
116 */
117static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], NDEBUG_UNUSED size_t outlen,
118 fr_sql_query_t *query_ctx)
119{
120 char errbuff[512];
121 int ret;
122 rlm_sql_oracle_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_oracle_conn_t);
123
124 fr_assert(outlen > 0);
125
126 ret = sql_snprint_error(errbuff, sizeof(errbuff), conn);
127 if (ret < 0) return 0;
128
129 out[0].type = L_ERR;
130 out[0].msg = talloc_strdup(ctx, errbuff);
131
132 return 1;
133}
134
135static int mod_detach(module_detach_ctx_t const *mctx)
136{
137 rlm_sql_oracle_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sql_oracle_t);
138
139 if (inst->env) OCIHandleFree((dvoid *)inst->env, OCI_HTYPE_ENV);
140
141 return 0;
142}
143
144static int mod_instantiate(module_inst_ctx_t const *mctx)
145{
146 rlm_sql_oracle_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sql_oracle_t);
147
148 /*
149 * Initialises the oracle environment
150 */
151 if (OCIEnvCreate(&inst->env, OCI_DEFAULT | OCI_THREADED, NULL, NULL, NULL, NULL, 0, NULL)) {
152 ERROR("Couldn't init Oracle OCI environment (OCIEnvCreate())");
153 return -1;
154 }
155
156 return 0;
157}
158
160{
161 char errbuff[512];
162
163 if (sql_snprint_error(errbuff, sizeof(errbuff), conn) < 0) return -1;
164
165 if (strstr(errbuff, "ORA-03113") || strstr(errbuff, "ORA-03114")) {
166 ERROR("OCI_SERVER_NOT_CONNECTED");
167 return RLM_SQL_RECONNECT;
168 }
169
170 return RLM_SQL_ERROR;
171}
172
173static void _sql_connection_close(UNUSED fr_event_list_t *el, void *h, UNUSED void *uctx)
174{
175 rlm_sql_oracle_conn_t *conn = talloc_get_type_abort(h, rlm_sql_oracle_conn_t);
176
177 if (conn->sess) {
178 OCISessionEnd(conn->ctx, conn->error, conn->sess, OCI_DEFAULT);
179 OCIHandleFree((dvoid *)conn->sess, OCI_HTYPE_SESSION);
180 }
181 if (conn->ctx) OCIHandleFree((dvoid *)conn->ctx, OCI_HTYPE_SVCCTX);
182 if (conn->srv) {
183 OCIServerDetach(conn->srv, conn->error, OCI_DEFAULT);
184 OCIHandleFree((dvoid *)conn->srv, OCI_HTYPE_SERVER);
185 }
186 if (conn->error) OCIHandleFree((dvoid *)conn->error, OCI_HTYPE_ERROR);
187
188 talloc_free(h);
189}
190
191#define ORACLE_ERROR(_message) \
192 sql_snprint_error(errbuff, sizeof(errbuff), c); \
193 ERROR(_message ": %s", errbuff); \
194 return CONNECTION_STATE_FAILED
195
196CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
197static connection_state_t _sql_connection_init(void **h, connection_t *conn, void *uctx)
198{
200 rlm_sql_oracle_t *inst = talloc_get_type_abort(sql->driver_submodule->data, rlm_sql_oracle_t);
202 char errbuff[512];
203 OraText *sql_password = NULL;
204 OraText *sql_login = NULL;
205
206 MEM(c = talloc_zero(conn, rlm_sql_oracle_conn_t));
207 *c = (rlm_sql_oracle_conn_t) {
208 .conn = conn,
209 .config = &sql->config,
210 .select_interval = 1000, /* Default starting poll interval - 1ms */
211 .query_interval = 1000,
212 };
213
214 /*
215 * Although there are simpler methods to start a connection using a connection
216 * pool, since we need to set an option on the server handle, to enable
217 * non-blocking mode, we have to follow this overly complicated sequence of
218 * handle creation.
219 */
220
221 /*
222 * Allocate an error handle
223 */
224 if (OCIHandleAlloc((dvoid *)inst->env, (dvoid **)&c->error, OCI_HTYPE_ERROR, 0, NULL) != OCI_SUCCESS) {
225 ERROR("Couldn't init Oracle ERROR handle (OCIHandleAlloc())");
227 }
228
229 /*
230 * Allocate a server handle and attache to a connection from the pool
231 */
232 if (OCIHandleAlloc((dvoid *)inst->env, (dvoid **)&c->srv, (ub4)OCI_HTYPE_SERVER, 0, NULL) != OCI_SUCCESS) {
233 ERROR("Couldn't allocate Oracle SERVER handle");
235 }
236 if (OCIServerAttach(c->srv, c->error, (CONST OraText *)sql->config.sql_db, strlen(sql->config.sql_db), (ub4)OCI_DEFAULT) != OCI_SUCCESS) {
237 ORACLE_ERROR("Failed to attach");
238 }
239
240 /*
241 * Allocate the service handle (which queries are run on) and associate it with the server
242 */
243 if (OCIHandleAlloc((dvoid *)inst->env, (dvoid **)&c->ctx, OCI_HTYPE_SVCCTX, 0, NULL) != OCI_SUCCESS) {
244 ERROR("Couldn't allocate Oracle SERVICE handle");
246 }
247 if (OCIAttrSet((dvoid *)c->ctx, OCI_HTYPE_SVCCTX, (dvoid *)c->srv, 0, OCI_ATTR_SERVER, c->error) != OCI_SUCCESS) {
248 ORACLE_ERROR("Failed to link service and server handles");
249 }
250
251
252 if (OCIHandleAlloc((dvoid *)inst->env, (dvoid **)&c->sess, OCI_HTYPE_SESSION, 0, NULL) != OCI_SUCCESS) {
253 ERROR("Couldn't allocate Oracle SESSION handle");
255 }
256
257 /*
258 * We need to fix const issues between 'const char *' vs 'unsigned char *'
259 */
260 memcpy(&sql_login, &c->config->sql_login, sizeof(sql_login));
261 memcpy(&sql_password, &c->config->sql_password, sizeof(sql_password));
262 if (OCIAttrSet((dvoid *)c->sess, OCI_HTYPE_SESSION, sql_login, strlen(c->config->sql_login),
263 OCI_ATTR_USERNAME, c->error) != OCI_SUCCESS) {
264 ORACLE_ERROR("Failed to set username");
265 }
266 if (OCIAttrSet((dvoid *)c->sess, OCI_HTYPE_SESSION, sql_password, strlen(c->config->sql_password),
267 OCI_ATTR_PASSWORD, c->error) != OCI_SUCCESS) {
268 ORACLE_ERROR("Failed to set password");
269 }
270
271 if (OCISessionBegin((dvoid *)c->ctx, c->error, c->sess, OCI_CRED_RDBMS, OCI_STMT_CACHE) != OCI_SUCCESS) {
272 ORACLE_ERROR("Failed to start session");
273 }
274
275 if (OCIAttrSet((dvoid *)c->ctx, OCI_HTYPE_SVCCTX, (dvoid *)c->sess, 0, OCI_ATTR_SESSION, c->error) != OCI_SUCCESS) {
276 ORACLE_ERROR("Failed to link service and session handles");
277 }
278
279 if (OCIAttrSet((dvoid *)c->ctx, OCI_HTYPE_SVCCTX, (dvoid *)&inst->stmt_cache_size, 0,
280 OCI_ATTR_STMTCACHESIZE, c->error) != OCI_SUCCESS) {
281 ORACLE_ERROR("Failed to set statement cache size");
282 }
283
284 /*
285 * Set the server to be non-blocking if we can.
286 */
287 if (OCIAttrSet((dvoid *)c->srv, OCI_HTYPE_SERVER, (dvoid *)0, 0, OCI_ATTR_NONBLOCKING_MODE, c->error) != OCI_SUCCESS) {
288 sql_snprint_error(errbuff, sizeof(errbuff), c);
289 WARN("Cound not set non-blocking mode: %s", errbuff);
290 }
291
292 *h = c;
293
295}
296
298
299CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
301{
302 rlm_sql_oracle_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_oracle_conn_t);
303 request_t *request;
304 trunk_request_t *treq;
305 fr_sql_query_t *query_ctx;
306 sword ret;
307 char errbuff[512];
308
309 if (trunk_connection_pop_request(&treq, tconn) != 0) return;
310 if (!treq) return;
311
312 query_ctx = talloc_get_type_abort(treq->preq, fr_sql_query_t);
313 request = query_ctx->request;
314
315 switch(query_ctx->status) {
317 ROPTIONAL(RDEBUG2, DEBUG2, "Executing query: %s", query_ctx->query_str);
318 if (OCIStmtPrepare2(sql_conn->ctx, &sql_conn->query, sql_conn->error,
319 (const OraText *)query_ctx->query_str, strlen(query_ctx->query_str),
320 NULL, 0, OCI_NTV_SYNTAX, OCI_DEFAULT)) {
321 sql_snprint_error(errbuff, sizeof(errbuff), sql_conn);
322 ERROR("Failed to prepare query: %s", errbuff);
324 return;
325 }
326
327 switch (query_ctx->type) {
328 case SQL_QUERY_SELECT:
329 ret = OCIStmtExecute(sql_conn->ctx, sql_conn->query, sql_conn->error, 0, 0, NULL, NULL, OCI_DEFAULT);
330 break;
331
332 default:
333 ret = OCIStmtExecute(sql_conn->ctx, sql_conn->query, sql_conn->error, 1, 0, NULL, NULL,
334 OCI_COMMIT_ON_SUCCESS);
335 break;
336 }
337 query_ctx->tconn = tconn;
338
339 switch (ret) {
340 case OCI_STILL_EXECUTING:
341 ROPTIONAL(RDEBUG3, DEBUG3, "Awaiting response");
342 query_ctx->status = SQL_QUERY_SUBMITTED;
343 sql_conn->query_ctx = query_ctx;
344 sql_conn->poll_count = 0;
346 return;
347
348 case OCI_SUCCESS:
349 query_ctx->rcode = RLM_SQL_OK;
350 break;
351
352 case OCI_NO_DATA:
353 query_ctx->rcode = RLM_SQL_NO_MORE_ROWS;
354 break;
355
356 default:
357 /*
358 * Error code 1 is unique contraint violated
359 */
360 if (sql_snprint_error(errbuff, sizeof(errbuff), sql_conn) == 1) {
361 query_ctx->rcode = RLM_SQL_ALT_QUERY;
362 break;
363 }
364 ERROR("SQL query failed: %s", errbuff);
366 if (sql_check_reconnect(sql_conn) == RLM_SQL_RECONNECT) {
368 }
369 return;
370 }
371 query_ctx->status = SQL_QUERY_RETURNED;
372 break;
373
374 default:
375 return;
376 }
377
378 ROPTIONAL(RDEBUG3, DEBUG3, "Got immediate response");
380 if (request) unlang_interpret_mark_runnable(request);
381}
382
383CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
384static void sql_request_cancel(connection_t *conn, void *preq, trunk_cancel_reason_t reason,
385 UNUSED void *uctx)
386{
387 fr_sql_query_t *query_ctx = talloc_get_type_abort(preq, fr_sql_query_t);
388 rlm_sql_oracle_conn_t *sql_conn= talloc_get_type_abort(conn->h, rlm_sql_oracle_conn_t);
389
390 if (!query_ctx->treq) return;
391 if (reason != TRUNK_CANCEL_REASON_SIGNAL) return;
392 if (sql_conn->query_ctx == query_ctx) sql_conn->query_ctx = NULL;
393}
394
395CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
397 connection_t *conn, UNUSED void *uctx)
398{
399 trunk_request_t *treq;
400 rlm_sql_oracle_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_oracle_conn_t);
401 sword status;
402
403 if ((trunk_connection_pop_cancellation(&treq, tconn)) != 0) return;
404 if (!treq) return;
405
406 /*
407 * Oracle cancels non-blocking operations with this pair of calls
408 * They operate on the service context handle, and since only one
409 * query will be on a connection at a time, that is what will be cancelled.
410 *
411 * It's not clear from the documentation as to whether this can return
412 * OCI_STILL_EXECUTING - so we allow for that.
413 */
414 status = OCIBreak(sql_conn->ctx, sql_conn->error);
415 switch (status) {
416 case OCI_STILL_EXECUTING:
418 return;
419
420 case OCI_SUCCESS:
421 break;
422
423 default:
424 {
425 char errbuff[512];
426 sql_snprint_error(errbuff, sizeof(errbuff), sql_conn);
427 ERROR("Failed cancelling query: %s", errbuff);
428 }
429 }
430 OCIReset(sql_conn->ctx, sql_conn->error) ;
431
433}
434
436{
437 rlm_sql_oracle_conn_t *c = talloc_get_type_abort(uctx, rlm_sql_oracle_conn_t);
438 fr_sql_query_t *query_ctx = c->query_ctx;
439 trunk_request_t *treq = query_ctx->treq;
440 request_t *request = query_ctx->request;
441 sword ret = OCI_SUCCESS;
442
443 switch (query_ctx->status) {
445 switch (query_ctx->type) {
446 case SQL_QUERY_SELECT:
447 ret = OCIStmtExecute(c->ctx, c->query, c->error, 0, 0, NULL, NULL, OCI_DEFAULT);
448 break;
449
450 default:
451 ret = OCIStmtExecute(c->ctx, c->query, c->error, 1, 0, NULL, NULL, OCI_COMMIT_ON_SUCCESS);
452 break;
453 }
454 c->poll_count++;
455 /* Back off the poll interval, up to half the query timeout */
456 if (c->poll_count > 2) {
457 if (query_ctx->type == SQL_QUERY_SELECT) {
459 } else {
461 }
462 }
463
464 switch (ret) {
465 case OCI_STILL_EXECUTING:
466 ROPTIONAL(RDEBUG3, DEBUG3, "Still awaiting response");
467 if (fr_event_timer_in(c, el, &c->read_ev,
470 ERROR("Unable to insert polling event");
471 }
472 return;
473
474 case OCI_SUCCESS:
475 case OCI_NO_DATA:
476 query_ctx->rcode = ret == OCI_NO_DATA ? RLM_SQL_NO_MORE_ROWS : RLM_SQL_OK;
477 /* If we only polled once, reduce the interval*/
478 if (c->poll_count == 1) {
479 if (query_ctx->type == SQL_QUERY_SELECT) {
480 c->select_interval /= 2;
481 } else {
482 c->query_interval /= 2;
483 }
484 }
485 break;
486
487 default:
488 {
489 char errbuff[512];
490 if (sql_snprint_error(errbuff, sizeof(errbuff), c) == 1) {
491 query_ctx->rcode = RLM_SQL_ALT_QUERY;
492 break;
493 }
494 ROPTIONAL(RERROR, ERROR, "Query failed: %s", errbuff);
495 query_ctx->status = SQL_QUERY_FAILED;
498 return;
499 }
500 }
501 break;
502
504 ret = OCIBreak(c->ctx, c->error);
505 if (ret == OCI_STILL_EXECUTING) {
506 ROPTIONAL(RDEBUG3, DEBUG3, "Still awaiting response");
509 ERROR("Unable to insert polling event");
510 }
511 return;
512 }
513 OCIReset(c->ctx, c->error);
515 return;
516
517 default:
518 return;
519 }
520
521 if (request) unlang_interpret_mark_runnable(request);
522}
523
525{
526 trunk_connection_t *tconn = talloc_get_type_abort(uctx, trunk_connection_t);
527
529}
530
531/*
532 * Oracle doesn't support event driven async, so in this case
533 * we have to resort to polling.
534 *
535 * This "notify" callback sets up the appropriate polling events.
536 */
537CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
539 trunk_connection_event_t notify_on, UNUSED void *uctx)
540{
541 rlm_sql_oracle_conn_t *c = talloc_get_type_abort(conn->h, rlm_sql_oracle_conn_t);
542 fr_sql_query_t *query_ctx = c->query_ctx;
543 uint poll_interval = (query_ctx && query_ctx->type != SQL_QUERY_SELECT) ? c->query_interval : c->select_interval;
544 switch (notify_on) {
548 return;
549
552 if (c->query_ctx) {
553 if (fr_event_timer_in(c, el, &c->read_ev, fr_time_delta_from_usec(poll_interval),
555 ERROR("Unable to insert polling event");
556 }
557 }
558 if (notify_on == TRUNK_CONN_EVENT_READ) return;
559
561
565 ERROR("Unable to insert polling event");
566 }
567 return;
568 }
569}
570
573
574static unlang_action_t sql_select_query_resume(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
575{
576 fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
577 rlm_sql_oracle_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_oracle_conn_t);
578 rlm_sql_row_t row = NULL;
579 sb2 *ind;
580 int i;
581 OCIParam *param;
582 OCIDefine *define;
583 ub2 dtype, dsize;
584
585 if (query_ctx->rcode != RLM_SQL_OK) RETURN_MODULE_FAIL;
586
587 /*
588 * We only need to do this once per result set, because
589 * the number of columns won't change.
590 */
591 if (conn->col_count == 0) {
592 if (OCIAttrGet((dvoid *)conn->query, OCI_HTYPE_STMT, (dvoid *)&conn->col_count, NULL,
593 OCI_ATTR_PARAM_COUNT, conn->error)) goto error;
594
595 if (conn->col_count == 0) goto error;
596 }
597
598 MEM(row = talloc_zero_array(conn, char*, conn->col_count + 1));
599 MEM(ind = talloc_zero_array(row, sb2, conn->col_count + 1));
600
601 for (i = 0; i < conn->col_count; i++) {
602 if (OCIParamGet(conn->query, OCI_HTYPE_STMT, conn->error, (dvoid **)&param, i + 1) != OCI_SUCCESS) {
603 ERROR("OCIParamGet() failed in sql_select_query");
604 goto error;
605 }
606
607 if (OCIAttrGet((dvoid*)param, OCI_DTYPE_PARAM, (dvoid*)&dtype, NULL, OCI_ATTR_DATA_TYPE,
608 conn->error) != OCI_SUCCESS) {
609 ERROR("OCIAttrGet() failed in sql_select_query");
610 goto error;
611 }
612
613 dsize = MAX_DATASTR_LEN;
614
615 /*
616 * Use the retrieved length of dname to allocate an output buffer, and then define the output
617 * variable (but only for char/string type columns).
618 */
619 switch (dtype) {
620#ifdef SQLT_AFC
621 case SQLT_AFC: /* ansii fixed char */
622#endif
623#ifdef SQLT_AFV
624 case SQLT_AFV: /* ansii var char */
625#endif
626 case SQLT_VCS: /* var char */
627 case SQLT_CHR: /* char */
628 case SQLT_STR: /* string */
629 if (OCIAttrGet((dvoid *)param, OCI_DTYPE_PARAM, (dvoid *)&dsize, NULL,
630 OCI_ATTR_DATA_SIZE, conn->error) != OCI_SUCCESS) {
631 ERROR("OCIAttrGet() failed in sql_select_query");
632 goto error;
633 }
634
636 case SQLT_DAT:
637 case SQLT_INT:
638 case SQLT_UIN:
639 case SQLT_FLT:
640 case SQLT_PDN:
641 case SQLT_BIN:
642 case SQLT_NUM:
643 MEM(row[i] = talloc_zero_array(row, char, dsize + 1));
644
645 break;
646 default:
647 dsize = 0;
648 row[i] = NULL;
649 break;
650 }
651
652 ind[i] = 0;
653
654 /*
655 * Grab the actual row value and write it to the buffer we allocated.
656 */
657 if (OCIDefineByPos(conn->query, &define, conn->error, i + 1, (ub1 *)row[i], dsize + 1, SQLT_STR,
658 (dvoid *)&ind[i], NULL, NULL, OCI_DEFAULT) != OCI_SUCCESS) {
659 ERROR("OCIDefineByPos() failed in sql_select_query");
660 goto error;
661 }
662 }
663
664 conn->row = row;
665 conn->ind = ind;
666
667 query_ctx->rcode = RLM_SQL_OK;
669
670 error:
671 talloc_free(row);
672
673 query_ctx->rcode = RLM_SQL_ERROR;
675}
676
677static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
678{
679 rlm_sql_oracle_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_oracle_conn_t);
680 int fields, i, status;
681 char const **names;
682 OCIParam *param;
683
684 if (OCIAttrGet((dvoid *)conn->query, OCI_HTYPE_STMT, (dvoid *)&fields, NULL, OCI_ATTR_PARAM_COUNT,
685 conn->error)) return RLM_SQL_ERROR;
686 if (fields == 0) return RLM_SQL_ERROR;
687
688 MEM(names = talloc_array(query_ctx, char const *, fields));
689
690 for (i = 0; i < fields; i++) {
691 OraText *pcol_name = NULL;
692 ub4 pcol_size = 0;
693
694 status = OCIParamGet(conn->query, OCI_HTYPE_STMT, conn->error, (dvoid **)&param, i + 1);
695 if (status != OCI_SUCCESS) {
696 ERROR("OCIParamGet(OCI_HTYPE_STMT) failed in sql_fields()");
697 error:
699
700 return RLM_SQL_ERROR;
701 }
702
703 status = OCIAttrGet((dvoid **)param, OCI_DTYPE_PARAM, &pcol_name, &pcol_size,
704 OCI_ATTR_NAME, conn->error);
705 if (status != OCI_SUCCESS) {
706 ERROR("OCIParamGet(OCI_ATTR_NAME) failed in sql_fields()");
707
708 goto error;
709 }
710
711 names[i] = (char const *)pcol_name;
712 }
713
714 *out = names;
715
716 return RLM_SQL_OK;
717}
718
720{
721 rlm_sql_oracle_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_oracle_conn_t);
722 ub4 rows = 0;
723 ub4 size = sizeof(ub4);
724
725 OCIAttrGet((CONST dvoid *)conn->query, OCI_HTYPE_STMT, (dvoid *)&rows, &size, OCI_ATTR_ROW_COUNT, conn->error);
726
727 return rows;
728}
729
730static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
731{
732 fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
733 int status = OCI_STILL_EXECUTING;
734 rlm_sql_oracle_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_oracle_conn_t);
735
736 if (!conn->ctx) {
737 ERROR("Socket not connected");
738
739 query_ctx->rcode = RLM_SQL_RECONNECT;
741 }
742
743 query_ctx->row = NULL;
744
745 while (status == OCI_STILL_EXECUTING) {
746 status = OCIStmtFetch(conn->query, conn->error, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
747 }
748 if (status == OCI_SUCCESS) {
749 query_ctx->row = conn->row;
750
751 query_ctx->rcode = RLM_SQL_OK;
753 }
754
755 if (status == OCI_NO_DATA) {
756 query_ctx->rcode = RLM_SQL_NO_MORE_ROWS;
758 }
759
760 if (status == OCI_ERROR) {
761 ERROR("fetch failed in sql_fetch_row");
762 query_ctx->rcode = sql_check_reconnect(conn);
764 }
765
766 query_ctx->rcode = RLM_SQL_ERROR;
768}
769
771{
772 rlm_sql_oracle_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_oracle_conn_t);
773 int status = OCI_STILL_EXECUTING;
774
775 /* Cancel the cursor first */
776 while (status == OCI_STILL_EXECUTING) {
777 status = OCIStmtFetch(conn->query, conn->error, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
778 }
779
780 TALLOC_FREE(conn->row);
781 conn->ind = NULL; /* ind is a child of row */
782 conn->col_count = 0;
783 conn->query_ctx = NULL;
784
785 if (OCIStmtRelease(conn->query, conn->error, NULL, 0, OCI_DEFAULT) != OCI_SUCCESS ) {
786 ERROR("OCI release failed in sql_finish_query");
787 return RLM_SQL_ERROR;
788 }
789
790 return RLM_SQL_OK;
791}
792
794{
795 rlm_sql_oracle_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_oracle_conn_t);
796
797 conn->query_ctx = NULL;
798
799 if (OCIStmtRelease(conn->query, conn->error, NULL, 0, OCI_DEFAULT) != OCI_SUCCESS ) {
800 ERROR("OCI release failed in sql_finish_query");
801 return RLM_SQL_ERROR;
802 }
803
804 return 0;
805}
806
808{
809 rlm_sql_oracle_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_oracle_conn_t);
810
811 TALLOC_FREE(conn->row);
812 conn->ind = NULL; /* ind is a child of row */
813 conn->col_count = 0;
814 conn->query_ctx = NULL;
815
816 if (OCIStmtRelease (conn->query, conn->error, NULL, 0, OCI_DEFAULT) != OCI_SUCCESS ) {
817 ERROR("OCI release failed in sql_finish_query");
818 return RLM_SQL_ERROR;
819 }
820
821 return 0;
822}
823
824/* Exported to rlm_sql */
827 .common = {
828 .name = "sql_oracle",
829 .magic = MODULE_MAGIC_INIT,
830 .inst_size = sizeof(rlm_sql_oracle_t),
833 .detach = mod_detach
834 },
836 .sql_query_resume = sql_query_resume,
837 .sql_select_query_resume = sql_select_query_resume,
838 .sql_num_rows = sql_num_rows,
839 .sql_affected_rows = sql_num_rows,
840 .sql_fetch_row = sql_fetch_row,
841 .sql_fields = sql_fields,
842 .sql_free_result = sql_free_result,
843 .sql_error = sql_error,
844 .sql_finish_query = sql_finish_query,
845 .sql_finish_select_query = sql_finish_select_query,
846 .trunk_io_funcs = {
847 .connection_alloc = sql_trunk_connection_alloc,
848 .connection_notify = sql_trunk_connection_notify,
849 .request_mux = sql_trunk_request_mux,
850 .request_cancel_mux = sql_request_cancel_mux,
851 .request_cancel = sql_request_cancel,
852 .request_fail = sql_request_fail,
853 }
854};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
#define define
#define RCSID(id)
Definition build.h:483
#define NDEBUG_UNUSED
Definition build.h:326
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:322
#define DIAG_ON(_x)
Definition build.h:458
#define CC_NO_UBSAN(_sanitize)
Definition build.h:426
#define UNUSED
Definition build.h:315
#define DIAG_OFF(_x)
Definition build.h:457
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:642
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:268
conf_parser_flags_t flags
Flags which control parsing behaviour.
Definition cf_parse.h:585
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:579
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
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
Definition connection.h:84
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:41
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
#define fr_event_timer_in(...)
Definition event.h:255
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:1359
#define ind(mm, x)
Definition isaac.c:18
#define DEBUG3(_fmt,...)
Definition log.h:266
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition log.h:528
#define RDEBUG3(fmt,...)
Definition log.h:343
#define RERROR(fmt,...)
Definition log.h:298
talloc_free(reap)
int fr_event_timer_delete(fr_event_timer_t const **ev_p)
Delete a timer event from the event list.
Definition event.c:1611
Stores all information relating to an event list.
Definition event.c:411
A timer event.
Definition event.c:102
@ L_ERR
Error message.
Definition log.h:56
unsigned int uint32_t
module_instance_t * mi
Module instance to detach.
Definition module_ctx.h:57
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for detach calls.
Definition module_ctx.h:56
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
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 WARN(fmt,...)
Definition radclient.h:47
#define RETURN_MODULE_OK
Definition rcode.h:57
#define RETURN_MODULE_FAIL
Definition rcode.h:56
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
static int instantiate(module_inst_ctx_t const *mctx)
Definition rlm_rest.c:1310
char const * sql_db
Database to run queries against.
Definition rlm_sql.h:78
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
fr_sql_query_type_t type
Type of query.
Definition rlm_sql.h:137
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_RECONNECT
Stale connection, should reconnect.
Definition rlm_sql.h:48
@ 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
@ SQL_QUERY_SELECT
Definition rlm_sql.h:114
fr_time_delta_t query_timeout
How long to allow queries to run for.
Definition rlm_sql.h:94
#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
char const * sql_login
Login credentials to use.
Definition rlm_sql.h:76
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
char const * sql_password
Login password to use.
Definition rlm_sql.h:77
trunk_request_t * treq
Trunk request for this query.
Definition rlm_sql.h:135
@ SQL_QUERY_CANCELLED
A cancellation has been sent to the server.
Definition rlm_sql.h:127
@ SQL_QUERY_RETURNED
Query has executed.
Definition rlm_sql.h:124
@ SQL_QUERY_FAILED
Failed to submit.
Definition rlm_sql.h:121
@ SQL_QUERY_SUBMITTED
Submitted for execution.
Definition rlm_sql.h:123
@ SQL_QUERY_PREPARED
Ready to submit.
Definition rlm_sql.h:122
Definition rlm_sql.h:61
static void sql_request_fail(request_t *request, void *preq, UNUSED void *rctx, UNUSED trunk_request_state_t state, UNUSED void *uctx)
rlm_sql_config_t const * config
SQL instance configuration.
int col_count
Number of columns associated with the result set.
OCISession * sess
Session handle.
rlm_sql_driver_t rlm_sql_oracle
static int mod_detach(module_detach_ctx_t const *mctx)
static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
OCISvcCtx * ctx
Service handle.
rlm_sql_row_t row
Results row.
static sql_rcode_t sql_finish_query(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
OCIServer * srv
Server handle.
fr_sql_query_t * query_ctx
Current request running on the connection.
static SQL_TRUNK_CONNECTION_ALLOC void sql_trunk_request_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
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 void sql_trunk_connection_write_poll(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
SQL_QUERY_FAIL static SQL_QUERY_RESUME unlang_action_t sql_select_query_resume(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
uint32_t stmt_cache_size
Statement cache size.
static connection_state_t _sql_connection_init(void **h, connection_t *conn, void *uctx)
static sql_rcode_t sql_finish_select_query(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
static int sql_snprint_error(char *out, size_t outlen, rlm_sql_oracle_conn_t *conn)
Write the last Oracle error out to a buffer.
static void sql_request_cancel(connection_t *conn, void *preq, trunk_cancel_reason_t reason, UNUSED void *uctx)
#define MAX_DATASTR_LEN
static int sql_num_rows(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
uint poll_count
How many polls have been done for the current query.
static void _sql_connection_close(UNUSED fr_event_list_t *el, void *h, UNUSED void *uctx)
static sql_rcode_t sql_check_reconnect(rlm_sql_oracle_conn_t *conn)
static void sql_request_cancel_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
OCIEnv * env
Environment handle.
OCIError * error
Error handle.
uint select_interval
How frequently this connection gets polled for select queries.
uint query_interval
How frequently this connection gets polled for other queries.
static void sql_trunk_connection_read_poll(fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
static const conf_parser_t driver_config[]
fr_event_timer_t const * read_ev
Timer event for polling reading this connection.
static sql_rcode_t sql_free_result(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
OCIStmt * query
Query handle.
#define ORACLE_ERROR(_message)
fr_event_timer_t const * write_ev
Timer event for polling writing this connection.
connection_t * conn
Generic connection structure for this connection.
static void sql_trunk_connection_notify(UNUSED trunk_connection_t *tconn, connection_t *conn, UNUSED fr_event_list_t *el, trunk_connection_event_t notify_on, UNUSED void *uctx)
static int mod_instantiate(module_inst_ctx_t const *mctx)
sb2 * ind
Indicators regarding contents of the results row.
Macros to reduce boilerplate in trunk SQL drivers.
#define SQL_QUERY_RESUME
#define SQL_TRUNK_CONNECTION_ALLOC
Allocate an SQL trunk connection.
#define SQL_QUERY_FAIL
void connection_signal_reconnect(connection_t *conn, connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
size_t inst_size
Size of the module's instance data.
Definition module.h:203
void * data
Module's instance data.
Definition module.h:271
eap_aka_sim_process_conf_t * inst
module_t common
Common fields for all loadable modules.
Definition rlm_sql.h:194
module_instance_t * driver_submodule
Driver's submodule.
Definition rlm_sql.h:227
rlm_sql_config_t config
Definition rlm_sql.h:221
#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_usec(fr_time_delta_t delta)
Definition time.h:632
static fr_time_delta_t fr_time_delta_from_usec(int64_t usec)
Definition time.h:568
"server local" time.
Definition time.h:69
int trunk_connection_pop_cancellation(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a cancellation request off a connection's cancellation queue.
Definition trunk.c:3835
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
Definition trunk.c:2132
void trunk_request_signal_cancel_sent(trunk_request_t *treq)
Signal that a remote server has been notified of the cancellation.
Definition trunk.c:2260
void trunk_request_signal_cancel_complete(trunk_request_t *treq)
Signal that a remote server acked our cancellation.
Definition trunk.c:2284
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_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
Definition trunk.c:2050
void trunk_connection_signal_writable(trunk_connection_t *tconn)
Signal that a trunk connection is writable.
Definition trunk.c:3903
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_connection_event_t
What type of I/O events the trunk connection is currently interested in receiving.
Definition trunk.h:72
@ TRUNK_CONN_EVENT_BOTH
Trunk should be notified if a connection is readable or writable.
Definition trunk.h:79
@ TRUNK_CONN_EVENT_WRITE
Trunk should be notified if a connection is writable.
Definition trunk.h:77
@ TRUNK_CONN_EVENT_NONE
Don't notify the trunk on connection state changes.
Definition trunk.h:73
@ TRUNK_CONN_EVENT_READ
Trunk should be notified if a connection is readable.
Definition trunk.h:75
trunk_cancel_reason_t
Reasons for a request being cancelled.
Definition trunk.h:55
@ TRUNK_CANCEL_REASON_SIGNAL
Request cancelled due to a signal.
Definition trunk.h:57
static fr_event_list_t * el
static size_t char ** out
Definition value.h:997