2 * sql.c rlm_sql - FreeRADIUS SQL Module
3 * Main code directly taken from ICRADIUS
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * Copyright 2001,2006 The FreeRADIUS server project
22 * Copyright 2000 Mike Machado <mike@innercite.com>
23 * Copyright 2000 Alan DeKok <aland@ox.org>
24 * Copyright 2001 Chad Miller <cmiller@surfsouth.com>
27 #include <freeradius-devel/ident.h>
30 #include <freeradius-devel/radiusd.h>
43 static void *sql_conn_create(void *ctx)
49 sqlsocket = rad_malloc(sizeof(*sqlsocket));
50 memset(sqlsocket, 0, sizeof(*sqlsocket));
52 rcode = (inst->module->sql_init_socket)(sqlsocket, inst->config);
54 exec_trigger(NULL, inst->cs, "modules.sql.open");
63 static int sql_conn_delete(void *ctx, void *connection)
66 SQLSOCK *sqlsocket = connection;
68 exec_trigger(NULL, inst->cs, "modules.sql.close");
70 if (sqlsocket->conn) {
71 (inst->module->sql_close)(sqlsocket, inst->config);
73 if (inst->module->sql_destroy_socket) {
74 (inst->module->sql_destroy_socket)(sqlsocket, inst->config);
82 /*************************************************************************
84 * Function: sql_init_socketpool
86 * Purpose: Connect to the sql server, if possible
88 *************************************************************************/
89 int sql_init_socketpool(SQL_INST * inst)
91 inst->pool = fr_connection_pool_init(inst->cs, inst,
95 if (!inst->pool) return -1;
100 /*************************************************************************
102 * Function: sql_poolfree
104 * Purpose: Clean up and free sql pool
106 *************************************************************************/
107 void sql_poolfree(SQL_INST * inst)
109 fr_connection_pool_delete(inst->pool);
113 /*************************************************************************
115 * Function: sql_get_socket
117 * Purpose: Return a SQL sqlsocket from the connection pool
119 *************************************************************************/
120 SQLSOCK * sql_get_socket(SQL_INST * inst)
122 return fr_connection_get(inst->pool);
125 /*************************************************************************
127 * Function: sql_release_socket
129 * Purpose: Frees a SQL sqlsocket back to the connection pool
131 *************************************************************************/
132 int sql_release_socket(SQL_INST * inst, SQLSOCK * sqlsocket)
134 fr_connection_release(inst->pool, sqlsocket);
139 /*************************************************************************
141 * Function: sql_userparse
143 * Purpose: Read entries from the database and fill VALUE_PAIR structures
145 *************************************************************************/
146 int sql_userparse(VALUE_PAIR ** first_pair, SQL_ROW row)
149 const char *ptr, *value;
150 char buf[MAX_STRING_LEN];
152 FR_TOKEN token, operator = T_EOL;
155 * Verify the 'Attribute' field
157 if (row[2] == NULL || row[2][0] == '\0') {
158 radlog(L_ERR, "rlm_sql: The 'Attribute' field is empty or NULL, skipping the entire row.");
163 * Verify the 'op' field
165 if (row[4] != NULL && row[4][0] != '\0') {
167 operator = gettoken(&ptr, buf, sizeof(buf));
168 if ((operator < T_OP_ADD) ||
169 (operator > T_OP_CMP_EQ)) {
170 radlog(L_ERR, "rlm_sql: Invalid operator \"%s\" for attribute %s", row[4], row[2]);
176 * Complain about empty or invalid 'op' field
178 operator = T_OP_CMP_EQ;
179 radlog(L_ERR, "rlm_sql: The 'op' field for attribute '%s = %s' is NULL, or non-existent.", row[2], row[3]);
180 radlog(L_ERR, "rlm_sql: You MUST FIX THIS if you want the configuration to behave as you expect.");
184 * The 'Value' field may be empty or NULL
188 * If we have a new-style quoted string, where the
189 * *entire* string is quoted, do xlat's.
191 if (row[3] != NULL &&
192 ((row[3][0] == '\'') || (row[3][0] == '`') || (row[3][0] == '"')) &&
193 (row[3][0] == row[3][strlen(row[3])-1])) {
195 token = gettoken(&value, buf, sizeof(buf));
198 * Take the unquoted string.
200 case T_SINGLE_QUOTED_STRING:
201 case T_DOUBLE_QUOTED_STRING:
206 * Mark the pair to be allocated later.
208 case T_BACK_QUOTED_STRING:
214 * Keep the original string.
225 pair = pairmake(row[2], value, operator);
227 radlog(L_ERR, "rlm_sql: Failed to create the pair: %s", fr_strerror());
231 pair->flags.do_xlat = 1;
232 strlcpy(pair->vp_strvalue, buf, sizeof(pair->vp_strvalue));
237 * Add the pair into the packet
239 pairadd(first_pair, pair);
244 /*************************************************************************
246 * Function: rlm_sql_fetch_row
248 * Purpose: call the module's sql_fetch_row and implement re-connect
250 *************************************************************************/
251 int rlm_sql_fetch_row(SQLSOCK *sqlsocket, SQL_INST *inst)
255 if (sqlsocket->conn) {
256 ret = (inst->module->sql_fetch_row)(sqlsocket, inst->config);
261 if (ret == SQL_DOWN) {
262 sqlsocket = fr_connection_reconnect(inst->pool, sqlsocket);
263 if (!sqlsocket) return -1;
265 /* retry the query on the newly connected socket */
266 ret = (inst->module->sql_fetch_row)(sqlsocket, inst->config);
269 radlog(L_ERR, "rlm_sql (%s): failed after re-connect",
270 inst->config->xlat_name);
278 /*************************************************************************
280 * Function: rlm_sql_query
282 * Purpose: call the module's sql_query and implement re-connect
284 *************************************************************************/
285 int rlm_sql_query(SQLSOCK *sqlsocket, SQL_INST *inst, char *query)
290 * If there's no query, return an error.
292 if (!query || !*query) {
296 if (sqlsocket->conn) {
297 ret = (inst->module->sql_query)(sqlsocket, inst->config, query);
302 if (ret == SQL_DOWN) {
303 sqlsocket = fr_connection_reconnect(inst->pool, sqlsocket);
304 if (!sqlsocket) return -1;
306 /* retry the query on the newly connected socket */
307 ret = (inst->module->sql_query)(sqlsocket, inst->config, query);
310 radlog(L_ERR, "rlm_sql (%s): failed after re-connect",
311 inst->config->xlat_name);
319 /*************************************************************************
321 * Function: rlm_sql_select_query
323 * Purpose: call the module's sql_select_query and implement re-connect
325 *************************************************************************/
326 int rlm_sql_select_query(SQLSOCK *sqlsocket, SQL_INST *inst, char *query)
331 * If there's no query, return an error.
333 if (!query || !*query) {
337 if (sqlsocket->conn) {
338 ret = (inst->module->sql_select_query)(sqlsocket, inst->config,
344 if (ret == SQL_DOWN) {
345 sqlsocket = fr_connection_reconnect(inst->pool, sqlsocket);
346 if (!sqlsocket) return -1;
348 /* retry the query on the newly connected socket */
349 ret = (inst->module->sql_select_query)(sqlsocket, inst->config, query);
352 radlog(L_ERR, "rlm_sql (%s): failed after re-connect",
353 inst->config->xlat_name);
362 /*************************************************************************
364 * Function: sql_getvpdata
366 * Purpose: Get any group check or reply pairs
368 *************************************************************************/
369 int sql_getvpdata(SQL_INST * inst, SQLSOCK * sqlsocket, VALUE_PAIR **pair, char *query)
374 if (rlm_sql_select_query(sqlsocket, inst, query)) {
375 radlog(L_ERR, "rlm_sql_getvpdata: database query error");
378 while (rlm_sql_fetch_row(sqlsocket, inst)==0) {
379 row = sqlsocket->row;
382 if (sql_userparse(pair, row) != 0) {
383 radlog(L_ERR | L_CONS, "rlm_sql (%s): Error getting data from database", inst->config->xlat_name);
384 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
389 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
394 void query_log(REQUEST *request, SQL_INST *inst, char *querystr)
396 FILE *sqlfile = NULL;
398 if (inst->config->sqltrace) {
401 if (!radius_xlat(buffer, sizeof(buffer),
402 inst->config->tracefile, request, NULL)) {
403 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
404 inst->config->xlat_name);
408 if ((sqlfile = fopen(buffer, "a")) == (FILE *) NULL) {
409 radlog(L_ERR, "rlm_sql (%s): Couldn't open file %s",
410 inst->config->xlat_name,
413 int fd = fileno(sqlfile);
415 rad_lockfd(fd, MAX_QUERY_LEN);
416 fputs(querystr, sqlfile);
417 fputs(";\n", sqlfile);
418 fclose(sqlfile); /* and release the lock */