예제 #1
0
/*
 *	Generic function for failing between a bunch of queries.
 *
 *	Uses the same principle as rlm_linelog, expanding the 'reference' config
 *	item using xlat to figure out what query it should execute.
 *
 *	If the reference matches multiple config items, and a query fails or
 *	doesn't update any rows, the next matching config item is used.
 *
 */
static int acct_redundant(rlm_sql_t *inst, REQUEST *request, sql_acct_section_t *section)
{
	rlm_rcode_t		rcode = RLM_MODULE_OK;

	rlm_sql_handle_t	*handle = NULL;
	int			sql_ret;
	int			numaffected = 0;

	CONF_ITEM		*item;
	CONF_PAIR 		*pair;
	char const		*attr = NULL;
	char const		*value;

	char			path[MAX_STRING_LEN];
	char			*p = path;
	char			*expanded = NULL;

	rad_assert(section);

	if (section->reference[0] != '.') {
		*p++ = '.';
	}

	if (radius_xlat(p, sizeof(path) - (p - path), request, section->reference, NULL, NULL) < 0) {
		rcode = RLM_MODULE_FAIL;

		goto finish;
	}

	item = cf_reference_item(NULL, section->cs, path);
	if (!item) {
		rcode = RLM_MODULE_FAIL;

		goto finish;
	}

	if (cf_item_is_section(item)){
		REDEBUG("Sections are not supported as references");
		rcode = RLM_MODULE_FAIL;

		goto finish;
	}

	pair = cf_itemtopair(item);
	attr = cf_pair_attr(pair);

	RDEBUG2("Using query template '%s'", attr);

	handle = sql_get_socket(inst);
	if (!handle) {
		rcode = RLM_MODULE_FAIL;

		goto finish;
	}

	sql_set_user(inst, request, NULL);

	while (true) {
		value = cf_pair_value(pair);
		if (!value) {
			RDEBUG("Ignoring null query");
			rcode = RLM_MODULE_NOOP;

			goto finish;
		}

		if (radius_axlat(&expanded, request, value, sql_escape_func, inst) < 0) {
			rcode = RLM_MODULE_FAIL;

			goto finish;
		}

		if (!*expanded) {
			RDEBUG("Ignoring null query");
			rcode = RLM_MODULE_NOOP;
			talloc_free(expanded);

			goto finish;
		}

		rlm_sql_query_log(inst, request, section, expanded);

		/*
		 *  If rlm_sql_query cannot use the socket it'll try and
		 *  reconnect. Reconnecting will automatically release
		 *  the current socket, and try to select a new one.
		 *
		 *  If we get RLM_SQL_RECONNECT it means all connections in the pool
		 *  were exhausted, and we couldn't create a new connection,
		 *  so we do not need to call sql_release_socket.
		 */
		sql_ret = rlm_sql_query(&handle, inst, expanded);
		TALLOC_FREE(expanded);

		if (sql_ret == RLM_SQL_RECONNECT) {
			rcode = RLM_MODULE_FAIL;

			goto finish;
		}
		rad_assert(handle);

		/*
		 *  Assume all other errors are incidental, and just meant our
		 *  operation failed and its not a client or SQL syntax error.
		 *
		 *  @fixme We should actually be able to distinguish between key
		 *  constraint violations (which we expect) and other errors.
		 */
		if (sql_ret == RLM_SQL_OK) {
			numaffected = (inst->module->sql_affected_rows)(handle, inst->config);
			if (numaffected > 0) {
				break;	/* A query succeeded, were done! */
			}

			RDEBUG("No records updated");
		}

		(inst->module->sql_finish_query)(handle, inst->config);

		/*
		 *  We assume all entries with the same name form a redundant
		 *  set of queries.
		 */
		pair = cf_pair_find_next(section->cs, pair, attr);

		if (!pair) {
			RDEBUG("No additional queries configured");
			rcode = RLM_MODULE_NOOP;

			goto finish;
		}

		RDEBUG("Trying next query...");
	}

	(inst->module->sql_finish_query)(handle, inst->config);

	finish:
	talloc_free(expanded);
	sql_release_socket(inst, handle);

	return rcode;
}
예제 #2
0
/*
 *			SQL xlat function
 *
 *  For selects the first value of the first column will be returned,
 *  for inserts, updates and deletes the number of rows afftected will be
 *  returned instead.
 */
static ssize_t sql_xlat(void *instance, REQUEST *request, char const *query, char *out, size_t freespace)
{
	rlm_sql_handle_t *handle = NULL;
	rlm_sql_row_t row;
	rlm_sql_t *inst = instance;
	ssize_t ret = 0;
	size_t len = 0;

	/*
	 *	Add SQL-User-Name attribute just in case it is needed
	 *	We could search the string fmt for SQL-User-Name to see if this is
	 * 	needed or not
	 */
	sql_set_user(inst, request, NULL);

	handle = sql_get_socket(inst);
	if (!handle) {
		return 0;
	}

	rlm_sql_query_log(inst, request, NULL, query);

	/*
	 *	If the query starts with any of the following prefixes,
	 *	then return the number of rows affected
	 */
	if ((strncasecmp(query, "insert", 6) == 0) ||
	    (strncasecmp(query, "update", 6) == 0) ||
	    (strncasecmp(query, "delete", 6) == 0)) {
		int numaffected;
		char buffer[21]; /* 64bit max is 20 decimal chars + null byte */

		if (rlm_sql_query(&handle, inst, query)) {
			char const *error = (inst->module->sql_error)(handle, inst->config);
			REDEBUG("SQL query failed: %s", error);

			ret = -1;
			goto finish;
		}

		numaffected = (inst->module->sql_affected_rows)(handle, inst->config);
		if (numaffected < 1) {
			RDEBUG("SQL query affected no rows");

			goto finish;
		}

		/*
		 *	Don't chop the returned number if freespace is
		 *	too small.  This hack is necessary because
		 *	some implementations of snprintf return the
		 *	size of the written data, and others return
		 *	the size of the data they *would* have written
		 *	if the output buffer was large enough.
		 */
		snprintf(buffer, sizeof(buffer), "%d", numaffected);

		len = strlen(buffer);
		if (len >= freespace){
			RDEBUG("rlm_sql (%s): Can't write result, insufficient string space", inst->config->xlat_name);

			(inst->module->sql_finish_query)(handle, inst->config);

			ret = -1;
			goto finish;
		}

		memcpy(out, buffer, len + 1); /* we did bounds checking above */
		ret = len;

		(inst->module->sql_finish_query)(handle, inst->config);

		goto finish;
	} /* else it's a SELECT statement */

	if (rlm_sql_select_query(&handle, inst, query)){
		char const *error = (inst->module->sql_error)(handle, inst->config);
		REDEBUG("SQL query failed: %s", error);
		ret = -1;

		goto finish;
	}

	ret = rlm_sql_fetch_row(&handle, inst);
	if (ret) {
		REDEBUG("SQL query failed");
		(inst->module->sql_finish_select_query)(handle, inst->config);
		ret = -1;

		goto finish;
	}

	row = handle->row;
	if (!row) {
		RDEBUG("SQL query returned no results");
		(inst->module->sql_finish_select_query)(handle, inst->config);
		ret = -1;

		goto finish;
	}

	if (!row[0]){
		RDEBUG("NULL value in first column of result");
		(inst->module->sql_finish_select_query)(handle, inst->config);
		ret = -1;

		goto finish;
	}

	len = strlen(row[0]);
	if (len >= freespace){
		RDEBUG("Insufficient string space");
		(inst->module->sql_finish_select_query)(handle, inst->config);

		ret = -1;
		goto finish;
	}

	strlcpy(out, row[0], freespace);
	ret = len;

	(inst->module->sql_finish_select_query)(handle, inst->config);

	finish:
	sql_release_socket(inst, handle);

	return ret;
}
예제 #3
0
/*
 *			SQL xlat function
 *
 *  For selects the first value of the first column will be returned,
 *  for inserts, updates and deletes the number of rows afftected will be
 *  returned instead.
 */
static size_t sql_xlat(void *instance, REQUEST *request,
		    const char *fmt, char *out, size_t freespace)
{
	rlm_sql_handle_t *handle;
	rlm_sql_row_t row;
	rlm_sql_t *inst = instance;
	char querystr[MAX_QUERY_LEN];
	size_t ret = 0;

	RDEBUG("sql_xlat");

	/*
         * Add SQL-User-Name attribute just in case it is needed
         *  We could search the string fmt for SQL-User-Name to see if this is
         *  needed or not
         */
	sql_set_user(inst, request, NULL);
	/*
	 * Do an xlat on the provided string (nice recursive operation).
	 */
	if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func, inst)) {
		radlog(L_ERR, "rlm_sql (%s): xlat failed.",
		       inst->config->xlat_name);
		return 0;
	}

	handle = sql_get_socket(inst);
	if (handle == NULL)
		return 0;

	rlm_sql_query_log(inst, request, NULL, querystr);

	/*
	 *	If the query starts with any of the following prefixes,
	 *	then return the number of rows affected
	 */
	if ((strncasecmp(querystr, "insert", 6) == 0) ||
	    (strncasecmp(querystr, "update", 6) == 0) ||
	    (strncasecmp(querystr, "delete", 6) == 0)) {
		int numaffected;
		char buffer[21]; /* 64bit max is 20 decimal chars + null byte */

		if (rlm_sql_query(&handle,inst,querystr)) {
			sql_release_socket(inst,handle);
			
			return 0;
		}
	       
		numaffected = (inst->module->sql_affected_rows)(handle,
								inst->config);
		if (numaffected < 1) {
			RDEBUG("rlm_sql (%s): SQL query affected no rows",
				inst->config->xlat_name);
		}

		/*
		 *	Don't chop the returned number if freespace is
		 *	too small.  This hack is necessary because
		 *	some implementations of snprintf return the
		 *	size of the written data, and others return
		 *	the size of the data they *would* have written
		 *	if the output buffer was large enough.
		 */
		snprintf(buffer, sizeof(buffer), "%d", numaffected);
		ret = strlen(buffer);
		if (ret >= freespace){
			RDEBUG("rlm_sql (%s): Can't write result, insufficient string space",
			       inst->config->xlat_name);
			(inst->module->sql_finish_query)(handle,
							 inst->config);
			sql_release_socket(inst,handle);
			return 0;
		}
		
		memcpy(out, buffer, ret + 1); /* we did bounds checking above */

		(inst->module->sql_finish_query)(handle, inst->config);
		sql_release_socket(inst,handle);
		return ret;
	} /* else it's a SELECT statement */

	if (rlm_sql_select_query(&handle,inst,querystr)){
		sql_release_socket(inst,handle);
		return 0;
	}

	ret = rlm_sql_fetch_row(&handle, inst);
	if (ret) {
		RDEBUG("SQL query did not succeed");
		(inst->module->sql_finish_select_query)(handle, inst->config);
		sql_release_socket(inst,handle);
		return 0;
	}

	row = handle->row;
	if (row == NULL) {
		RDEBUG("SQL query did not return any results");
		(inst->module->sql_finish_select_query)(handle, inst->config);
		sql_release_socket(inst,handle);
		return 0;
	}

	if (row[0] == NULL){
		RDEBUG("Null value in first column");
		(inst->module->sql_finish_select_query)(handle, inst->config);
		sql_release_socket(inst,handle);
		return 0;
	}
	ret = strlen(row[0]);
	if (ret >= freespace){
		RDEBUG("Insufficient string space");
		(inst->module->sql_finish_select_query)(handle, inst->config);
		sql_release_socket(inst,handle);
		return 0;
	}

	strlcpy(out,row[0],freespace);

	RDEBUG("sql_xlat finished");

	(inst->module->sql_finish_select_query)(handle, inst->config);
	sql_release_socket(inst,handle);
	return ret;
}
예제 #4
0
/*
 *	Generic function for failing between a bunch of queries.
 *
 *	Uses the same principle as rlm_linelog, expanding the 'reference' config
 *	item using xlat to figure out what query it should execute.
 *
 *	If the reference matches multiple config items, and a query fails or
 *	doesn't update any rows, the next matching config item is used.
 *  
 */
static int acct_redundant(rlm_sql_t *inst, REQUEST *request, 
			  sql_acct_section_t *section)
{
	int	ret = RLM_MODULE_OK;

	rlm_sql_handle_t	*handle = NULL;
	int	sql_ret;
	int	numaffected = 0;

	CONF_ITEM  *item;
	CONF_PAIR  *pair;
	const char *attr = NULL;
	const char *value;

	char	path[MAX_STRING_LEN];
	char	querystr[MAX_QUERY_LEN];
	
	char	*p = path;

	rad_assert(section);
	
	if (section->reference[0] != '.')
		*p++ = '.';
	
	if (!radius_xlat(p, (sizeof(path) - (p - path)) - 1,
			section->reference, request, NULL, NULL))
		return RLM_MODULE_FAIL;

	item = cf_reference_item(NULL, section->cs, path);
	if (!item)
		return RLM_MODULE_FAIL;

	if (cf_item_is_section(item)){
		radlog(L_ERR, "Sections are not supported as references");
		
		return RLM_MODULE_FAIL;
	}
	
	pair = cf_itemtopair(item);
	attr = cf_pair_attr(pair);
	
	RDEBUG2("Using query template '%s'", attr);
	
	handle = sql_get_socket(inst);
	if (handle == NULL)
		return RLM_MODULE_FAIL;
		
	sql_set_user(inst, request, NULL);

	while (TRUE) {
		value = cf_pair_value(pair);
		if (!value) {
			RDEBUG("Ignoring null query");
			ret = RLM_MODULE_NOOP;
			
			goto release;
		}
		
		radius_xlat(querystr, sizeof(querystr), value, request,
			    sql_escape_func, inst);
		if (!*querystr) {
			RDEBUG("Ignoring null query");
			ret = RLM_MODULE_NOOP;
			
			goto release;
		}
		
		rlm_sql_query_log(inst, request, section, querystr);
		
		/*
		 *  If rlm_sql_query cannot use the socket it'll try and
		 *  reconnect. Reconnecting will automatically release 
		 *  the current socket, and try to select a new one.
		 *
		 *  If we get SQL_DOWN it means all connections in the pool
		 *  were exhausted, and we couldn't create a new connection,
		 *  so we do not need to call sql_release_socket.
		 */
		sql_ret = rlm_sql_query(&handle, inst, querystr);	
		if (sql_ret == SQL_DOWN)
			return RLM_MODULE_FAIL;
		
		rad_assert(handle);
	
		/* 
		 *  Assume all other errors are incidental, and just meant our
		 *  operation failed and its not a client or SQL syntax error.
		 */
		if (sql_ret == 0) {
			numaffected = (inst->module->sql_affected_rows)
					(handle, inst->config);
			if (numaffected > 0)
				break;
				
			RDEBUG("No records updated");
		}

		(inst->module->sql_finish_query)(handle, inst->config);
		
		/*
		 *  We assume all entries with the same name form a redundant
		 *  set of queries.
		 */
		pair = cf_pair_find_next(section->cs, pair, attr);
		
		if (!pair) {
			RDEBUG("No additional queries configured");
			
			ret = RLM_MODULE_NOOP;
			
			goto release;
		}

		RDEBUG("Trying next query...");
	}
	
	(inst->module->sql_finish_query)(handle, inst->config);

	release:
	sql_release_socket(inst, handle);

	return ret;
}
예제 #5
0
/** Executes a SELECT query and maps the result to server attributes
 *
 * @param mod_inst #rlm_sql_t instance.
 * @param proc_inst Instance data for this specific mod_proc call (unused).
 * @param request The current request.
 * @param query string to execute.
 * @param maps Head of the map list.
 * @return
 *	- #RLM_MODULE_NOOP no rows were returned or columns matched.
 *	- #RLM_MODULE_UPDATED if one or more #VALUE_PAIR were added to the #REQUEST.
 *	- #RLM_MODULE_FAIL if a fault occurred.
 */
static rlm_rcode_t mod_map_proc(void *mod_inst, UNUSED void *proc_inst, REQUEST *request,
				char const *query, vp_map_t const *maps)
{
	rlm_sql_t		*inst = talloc_get_type_abort(mod_inst, rlm_sql_t);
	rlm_sql_handle_t	*handle = NULL;

	int			i, j;

	rlm_rcode_t		rcode = RLM_MODULE_UPDATED;
	sql_rcode_t		ret;

	vp_map_t const		*map;

	rlm_sql_row_t		row;

	int			rows;
	int			field_cnt;
	char const		**fields = NULL, *map_rhs;
	char			map_rhs_buff[128];

#define MAX_SQL_FIELD_INDEX (64)

	int			field_index[MAX_SQL_FIELD_INDEX];
	bool			found_field = false;	/* Did we find any matching fields in the result set ? */

	rad_assert(inst->module->sql_fields);		/* Should have been caught during validation... */

	for (i = 0; i < MAX_SQL_FIELD_INDEX; i++) field_index[i] = -1;

	/*
	 *	Add SQL-User-Name attribute just in case it is needed
	 *	We could search the string fmt for SQL-User-Name to see if this is
	 * 	needed or not
	 */
	sql_set_user(inst, request, NULL);

	handle = fr_connection_get(inst->pool);		/* connection pool should produce error */
	if (!handle) return 0;

	rlm_sql_query_log(inst, request, NULL, query);

	ret = rlm_sql_select_query(inst, request, &handle, query);
	if (ret != RLM_SQL_OK) {
		RERROR("SQL query failed: %s", fr_int2str(sql_rcode_table, ret, "<INVALID>"));
		rcode = RLM_MODULE_FAIL;
		goto finish;
	}

	ret = (inst->module->sql_fields)(&fields, handle, inst->config);
	if (ret != RLM_SQL_OK) {
		RERROR("Failed retrieving field names: %s", fr_int2str(sql_rcode_table, ret, "<INVALID>"));
	error:
		rcode = RLM_MODULE_FAIL;
		(inst->module->sql_finish_select_query)(handle, inst->config);
		goto finish;
	}
	rad_assert(fields);
	field_cnt = talloc_array_length(fields);

	if (RDEBUG_ENABLED3) for (j = 0; j < field_cnt; j++) RDEBUG3("Got field: %s", fields[j]);

	/*
	 *	Iterate over the maps, it's O(N2)ish but probably
	 *	faster than building a radix tree each time the
	 *	map set is evaluated (map->rhs can be dynamic).
	 */
	for (map = maps, i = 0;
	     map && (i < MAX_SQL_FIELD_INDEX);
	     map = map->next, i++) {
		/*
		 *	Expand the RHS to get the name of the SQL field
		 */
		if (tmpl_expand(&map_rhs, map_rhs_buff, sizeof(map_rhs_buff),
				request, map->rhs, NULL, NULL) < 0) {
			RERROR("Failed getting field name: %s", fr_strerror());
			goto error;
		}

		for (j = 0; j < field_cnt; j++) {
			if (strcmp(fields[j], map_rhs) != 0) continue;
			field_index[i] = j;
			found_field = true;
		}
	}

	/*
	 *	Couldn't resolve any map RHS values to fields
	 *	in the result set.
	 */
	if (!found_field) {
		RDEBUG("No fields matching map found in query result");
		rcode = RLM_MODULE_NOOP;
		(inst->module->sql_finish_select_query)(handle, inst->config);
		goto finish;
	}

	/*
	 *	We've resolved all the maps to result indexes, now convert
	 *	the values at those indexes into VALUE_PAIRs.
	 *
	 *	Note: Not all SQL client libraries provide a row count,
	 *	so we have to do the count here.
	 */
	for (ret = rlm_sql_fetch_row(&row, inst, request, &handle), rows = 0;
	     (ret == RLM_SQL_OK) && row;
	     ret = rlm_sql_fetch_row(&row, inst, request, &handle), rows++) {
		for (map = maps, j = 0;
		     map && (j < MAX_SQL_FIELD_INDEX);
		     map = map->next, j++) {
			if (field_index[j] < 0) continue;	/* We didn't find the map RHS in the field set */
			if (map_to_request(request, map, _sql_map_proc_get_value, row[field_index[j]]) < 0) goto error;
		}
	}

	if (ret == RLM_SQL_ERROR) goto error;

	if (!rows) {
		RDEBUG("SQL query returned no results");
		rcode = RLM_MODULE_NOOP;
	}

	(inst->module->sql_finish_select_query)(handle, inst->config);

finish:
	talloc_free(fields);
	fr_connection_release(inst->pool, handle);

	return rcode;
}
예제 #6
0
/*
 *	Generic function for failing between a bunch of queries.
 *
 *	Uses the same principle as rlm_linelog, expanding the 'reference' config
 *	item using xlat to figure out what query it should execute.
 *
 *	If the reference matches multiple config items, and a query fails or
 *	doesn't update any rows, the next matching config item is used.
 *
 */
static int acct_redundant(rlm_sql_t *inst, REQUEST *request, sql_acct_section_t *section)
{
	rlm_rcode_t		rcode = RLM_MODULE_OK;

	rlm_sql_handle_t	*handle = NULL;
	int			sql_ret;
	int			numaffected = 0;

	CONF_ITEM		*item;
	CONF_PAIR 		*pair;
	char const		*attr = NULL;
	char const		*value;

	char			path[MAX_STRING_LEN];
	char			*p = path;
	char			*expanded = NULL;

	rad_assert(section);

	if (section->reference[0] != '.') {
		*p++ = '.';
	}

	if (radius_xlat(p, sizeof(path) - (p - path), request, section->reference, NULL, NULL) < 0) {
		rcode = RLM_MODULE_FAIL;

		goto finish;
	}

	/*
	 *	If we can't find a matching config item we do
	 *	nothing so return RLM_MODULE_NOOP.
	 */
	item = cf_reference_item(NULL, section->cs, path);
	if (!item) {
		RWDEBUG("No such configuration item %s", path);
		rcode = RLM_MODULE_NOOP;

		goto finish;
	}
	if (cf_item_is_section(item)){
		RWDEBUG("Sections are not supported as references");
		rcode = RLM_MODULE_NOOP;

		goto finish;
	}

	pair = cf_item_to_pair(item);
	attr = cf_pair_attr(pair);

	RDEBUG2("Using query template '%s'", attr);

	handle = fr_connection_get(inst->pool);
	if (!handle) {
		rcode = RLM_MODULE_FAIL;

		goto finish;
	}

	sql_set_user(inst, request, NULL);

	while (true) {
		value = cf_pair_value(pair);
		if (!value) {
			RDEBUG("Ignoring null query");
			rcode = RLM_MODULE_NOOP;

			goto finish;
		}

		if (radius_axlat(&expanded, request, value, inst->sql_escape_func, handle) < 0) {
			rcode = RLM_MODULE_FAIL;

			goto finish;
		}

		if (!*expanded) {
			RDEBUG("Ignoring null query");
			rcode = RLM_MODULE_NOOP;
			talloc_free(expanded);

			goto finish;
		}

		rlm_sql_query_log(inst, request, section, expanded);

		sql_ret = rlm_sql_query(inst, request, &handle, expanded);
		TALLOC_FREE(expanded);
		RDEBUG("SQL query returned: %s", fr_int2str(sql_rcode_table, sql_ret, "<INVALID>"));

		switch (sql_ret) {
		/*
		 *  Query was a success! Now we just need to check if it did anything.
		 */
		case RLM_SQL_OK:
			break;

		/*
		 *  A general, unrecoverable server fault.
		 */
		case RLM_SQL_ERROR:
		/*
		 *  If we get RLM_SQL_RECONNECT it means all connections in the pool
		 *  were exhausted, and we couldn't create a new connection,
		 *  so we do not need to call fr_connection_release.
		 */
		case RLM_SQL_RECONNECT:
			rcode = RLM_MODULE_FAIL;
			goto finish;

		/*
		 *  Query was invalid, this is a terminal error, but we still need
		 *  to do cleanup, as the connection handle is still valid.
		 */
		case RLM_SQL_QUERY_INVALID:
			rcode = RLM_MODULE_INVALID;
			goto finish;

		/*
		 *  Driver found an error (like a unique key constraint violation)
		 *  that hinted it might be a good idea to try an alternative query.
		 */
		case RLM_SQL_ALT_QUERY:
			goto next;
		}
		rad_assert(handle);

		/*
		 *  We need to have updated something for the query to have been
		 *  counted as successful.
		 */
		numaffected = (inst->module->sql_affected_rows)(handle, inst->config);
		(inst->module->sql_finish_query)(handle, inst->config);
		RDEBUG("%i record(s) updated", numaffected);

		if (numaffected > 0) break;	/* A query succeeded, were done! */
	next:
		/*
		 *  We assume all entries with the same name form a redundant
		 *  set of queries.
		 */
		pair = cf_pair_find_next(section->cs, pair, attr);

		if (!pair) {
			RDEBUG("No additional queries configured");
			rcode = RLM_MODULE_NOOP;

			goto finish;
		}

		RDEBUG("Trying next query...");
	}


finish:
	talloc_free(expanded);
	fr_connection_release(inst->pool, handle);
	sql_unset_user(inst, request);

	return rcode;
}
예제 #7
0
/** Execute an arbitrary SQL query
 *
 *  For selects the first value of the first column will be returned,
 *  for inserts, updates and deletes the number of rows affected will be
 *  returned instead.
 */
static ssize_t sql_xlat(char **out, UNUSED size_t outlen,
			void const *mod_inst, UNUSED void const *xlat_inst,
			REQUEST *request, char const *fmt)
{
	rlm_sql_handle_t	*handle = NULL;
	rlm_sql_row_t		row;
	rlm_sql_t const		*inst = mod_inst;
	sql_rcode_t		rcode;
	ssize_t			ret = 0;

	/*
	 *	Add SQL-User-Name attribute just in case it is needed
	 *	We could search the string fmt for SQL-User-Name to see if this is
	 * 	needed or not
	 */
	sql_set_user(inst, request, NULL);

	handle = fr_connection_get(inst->pool);	/* connection pool should produce error */
	if (!handle) return 0;

	rlm_sql_query_log(inst, request, NULL, fmt);

	/*
	 *	If the query starts with any of the following prefixes,
	 *	then return the number of rows affected
	 */
	if ((strncasecmp(fmt, "insert", 6) == 0) ||
	    (strncasecmp(fmt, "update", 6) == 0) ||
	    (strncasecmp(fmt, "delete", 6) == 0)) {
		int numaffected;

		rcode = rlm_sql_query(inst, request, &handle, fmt);
		if (rcode != RLM_SQL_OK) {
		query_error:
			RERROR("SQL query failed: %s", fr_int2str(sql_rcode_table, rcode, "<INVALID>"));

			ret = -1;
			goto finish;
		}

		numaffected = (inst->module->sql_affected_rows)(handle, inst->config);
		if (numaffected < 1) {
			RDEBUG("SQL query affected no rows");

			goto finish;
		}

		MEM(*out = talloc_asprintf(request, "%d", numaffected));
		ret = talloc_array_length(*out) - 1;

		(inst->module->sql_finish_query)(handle, inst->config);

		goto finish;
	} /* else it's a SELECT statement */

	rcode = rlm_sql_select_query(inst, request, &handle, fmt);
	if (rcode != RLM_SQL_OK) goto query_error;

	rcode = rlm_sql_fetch_row(&row, inst, request, &handle);
	if (rcode) {
		(inst->module->sql_finish_select_query)(handle, inst->config);
		goto query_error;
	}

	if (!row) {
		RDEBUG("SQL query returned no results");
		(inst->module->sql_finish_select_query)(handle, inst->config);
		ret = -1;

		goto finish;
	}

	if (!row[0]){
		RDEBUG("NULL value in first column of result");
		(inst->module->sql_finish_select_query)(handle, inst->config);
		ret = -1;

		goto finish;
	}

	*out = talloc_bstrndup(request, row[0], strlen(row[0]));
	ret = talloc_array_length(*out) - 1;

	(inst->module->sql_finish_select_query)(handle, inst->config);

finish:
	fr_connection_release(inst->pool, handle);

	return ret;
}