예제 #1
0
/*
 *	Execute postauth_query after authentication
 */
static int rlm_sql_postauth(void *instance, REQUEST *request) {
	SQLSOCK 	*sqlsocket = NULL;
	SQL_INST	*inst = instance;
	char		querystr[MAX_QUERY_LEN];
	char		sqlusername[MAX_STRING_LEN];

	DEBUG("rlm_sql (%s): Processing sql_postauth", inst->config->xlat_name);

	if(sql_set_user(inst, request, sqlusername, NULL) < 0)
		return RLM_MODULE_FAIL;

	/* If postauth_query is not defined, we stop here */
	if (inst->config->postauth_query[0] == '\0')
		return RLM_MODULE_NOOP;

	/* Expand variables in the query */
	memset(querystr, 0, MAX_QUERY_LEN);
	radius_xlat(querystr, sizeof(querystr), inst->config->postauth_query,
		    request, sql_escape_func);
	query_log(request, inst, querystr);
	DEBUG2("rlm_sql (%s) in sql_postauth: query is %s",
	       inst->config->xlat_name, querystr);

	/* Initialize the sql socket */
	sqlsocket = sql_get_socket(inst);
	if (sqlsocket == NULL)
		return RLM_MODULE_FAIL;

	/* Process the query */
	if (rlm_sql_query(sqlsocket, inst, querystr)) {
		radlog(L_ERR, "rlm_sql (%s) in sql_postauth: Database query error - %s",
		       inst->config->xlat_name,
		       (char *)(inst->module->sql_error)(sqlsocket, inst->config));
		sql_release_socket(inst, sqlsocket);
		return RLM_MODULE_FAIL;
	}
	(inst->module->sql_finish_query)(sqlsocket, inst->config);

	sql_release_socket(inst, sqlsocket);
	return RLM_MODULE_OK;
}
예제 #2
0
/*
 *	Execute postauth_query after authentication
 */
static rlm_rcode_t rlm_redisn_postauth(void *instance, REQUEST *request) {
	REDISSOCK 	*redis_socket = NULL;
	REDIS_INST	*inst = instance;
	char		querystr[MAX_QUERY_LEN];
	char		redisnusername[MAX_STRING_LEN];

	/* If postauth_query is not defined, we stop here */
	if (!inst->postauth_query ||
	    (inst->postauth_query[0] == '\0'))
		return RLM_MODULE_NOOP;

	if(redisn_set_user(inst, request, redisnusername, NULL) < 0)
		return RLM_MODULE_FAIL;

	/* Expand variables in the query */
	memset(querystr, 0, MAX_QUERY_LEN);
	radius_xlat(querystr, sizeof(querystr), inst->postauth_query,
		    request, redisn_escape_func, inst);
	query_log(request, inst, querystr);
	DEBUG2("rlm_redisn (%s) in redisn_postauth: query is %s",
	       inst->xlat_name, querystr);

	/* Initialize the redisn socket */
	redis_socket = redisn_get_socket(inst);
	if (redis_socket == NULL)
		return RLM_MODULE_FAIL;

	/* Process the query */
	if (rlm_redisn_query(inst, redis_socket, querystr)) {
		radlog(L_ERR, "rlm_redisn (%s) in redisn_postauth: Database query error - %s",
		       inst->xlat_name,
		       querystr);
		redisn_release_socket(inst, redis_socket);
		return RLM_MODULE_FAIL;
	}
	(inst->redisn_finish_query)(inst, redis_socket);

	redisn_release_socket(inst, redis_socket);
	return RLM_MODULE_OK;
}
예제 #3
0
/*
 *	Accounting: save the account data to our sql table
 */
static int rlm_sql_accounting(void *instance, REQUEST * request) {

	SQLSOCK *sqlsocket = NULL;
	VALUE_PAIR *pair;
	SQL_INST *inst = instance;
	int	ret = RLM_MODULE_OK;
	int     numaffected = 0;
	int     acctstatustype = 0;
	char    querystr[MAX_QUERY_LEN];
	char    logstr[MAX_QUERY_LEN];
	char	sqlusername[MAX_STRING_LEN];

#ifdef CISCO_ACCOUNTING_HACK
	int     acctsessiontime = 0;
#endif

	memset(querystr, 0, MAX_QUERY_LEN);

	/*
	 * Find the Acct Status Type
	 */
	if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) {
		acctstatustype = pair->lvalue;
	} else {
		radius_xlat(logstr, sizeof(logstr), "packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
		radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s",
		       inst->config->xlat_name, logstr);
		return RLM_MODULE_INVALID;
	}

	switch (acctstatustype) {
			/*
			 * The Terminal server informed us that it was rebooted
			 * STOP all records from this NAS
			 */
		case PW_STATUS_ACCOUNTING_ON:
		case PW_STATUS_ACCOUNTING_OFF:
			radlog(L_INFO, "rlm_sql (%s): received Acct On/Off packet", inst->config->xlat_name);
			radius_xlat(querystr, sizeof(querystr), inst->config->accounting_onoff_query, request, sql_escape_func);
			query_log(request, inst, querystr);

			sqlsocket = sql_get_socket(inst);
			if (sqlsocket == NULL)
				return(RLM_MODULE_FAIL);
			if (*querystr) { /* non-empty query */
				if (rlm_sql_query(sqlsocket, inst, querystr)) {
					radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting for Acct On/Off packet - %s",
					       inst->config->xlat_name,
					       (char *)(inst->module->sql_error)(sqlsocket, inst->config));
					ret = RLM_MODULE_FAIL;
				}
				(inst->module->sql_finish_query)(sqlsocket, inst->config);
			}

			break;

			/*
			 * Got an update accounting packet
			 */
		case PW_STATUS_ALIVE:

			/*
			 * Set, escape, and check the user attr here
			 */
			sql_set_user(inst, request, sqlusername, NULL);

			radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query, request, sql_escape_func);
			query_log(request, inst, querystr);

			sqlsocket = sql_get_socket(inst);
			if (sqlsocket == NULL)
				return(RLM_MODULE_FAIL);
			if (*querystr) { /* non-empty query */
				if (rlm_sql_query(sqlsocket, inst, querystr)) {
					radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting ALIVE record - %s",
					       inst->config->xlat_name,
					       (char *)(inst->module->sql_error)(sqlsocket, inst->config));
					ret = RLM_MODULE_FAIL;
				}
				else {
					numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
					if (numaffected < 1) {

						/*
						 * If our update above didn't match anything
						 * we assume it's because we haven't seen a
						 * matching Start record.  So we have to
						 * insert this update rather than do an update
						 */
						radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query_alt, request, sql_escape_func);
						query_log(request, inst, querystr);
						if (*querystr) { /* non-empty query */
							if (rlm_sql_query(sqlsocket, inst, querystr)) {
								radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting ALIVE record - %s",
									   inst->config->xlat_name,
									   (char *)(inst->module->sql_error)(sqlsocket, inst->config));
								ret = RLM_MODULE_FAIL;
							}
							(inst->module->sql_finish_query)(sqlsocket, inst->config);
						}
					}
				}
				(inst->module->sql_finish_query)(sqlsocket, inst->config);
			}
			break;

			/*
			 * Got accounting start packet
			 */
		case PW_STATUS_START:

			/*
			 * Set, escape, and check the user attr here
			 */
			sql_set_user(inst, request, sqlusername, NULL);

			radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query, request, sql_escape_func);
			query_log(request, inst, querystr);

			sqlsocket = sql_get_socket(inst);
			if (sqlsocket == NULL)
				return(RLM_MODULE_FAIL);
			if (*querystr) { /* non-empty query */
				if (rlm_sql_query(sqlsocket, inst, querystr)) {
					radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting START record - %s",
					       inst->config->xlat_name,
					       (char *)(inst->module->sql_error)(sqlsocket, inst->config));

					/*
					 * We failed the insert above.  It's probably because
					 * the stop record came before the start.  We try
					 * our alternate query now (typically an UPDATE)
					 */
					radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query_alt, request, sql_escape_func);
					query_log(request, inst, querystr);

					if (*querystr) { /* non-empty query */
						if (rlm_sql_query(sqlsocket, inst, querystr)) {
							radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting START record - %s",
							       inst->config->xlat_name,
							       (char *)(inst->module->sql_error)(sqlsocket, inst->config));
							ret = RLM_MODULE_FAIL;
						}
						(inst->module->sql_finish_query)(sqlsocket, inst->config);
					}
				}
				(inst->module->sql_finish_query)(sqlsocket, inst->config);
			}
			break;

			/*
			 * Got accounting stop packet
			 */
		case PW_STATUS_STOP:

			/*
			 * Set, escape, and check the user attr here
			 */
			sql_set_user(inst, request, sqlusername, NULL);

			radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query, request, sql_escape_func);
			query_log(request, inst, querystr);

			sqlsocket = sql_get_socket(inst);
			if (sqlsocket == NULL)
				return(RLM_MODULE_FAIL);
			if (*querystr) { /* non-empty query */
				if (rlm_sql_query(sqlsocket, inst, querystr)) {
					radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting STOP record - %s",
					       inst->config->xlat_name,
					       (char *)(inst->module->sql_error)(sqlsocket, inst->config));
					ret = RLM_MODULE_FAIL;
				}
				else {
					numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
					if (numaffected < 1) {
						/*
						 * If our update above didn't match anything
						 * we assume it's because we haven't seen a
						 * matching Start record.  So we have to
						 * insert this stop rather than do an update
						 */
#ifdef CISCO_ACCOUNTING_HACK
					        /*
					         * If stop but zero session length AND no previous
					         * session found, drop it as in invalid packet
				        	 * This is to fix CISCO's aaa from filling our
				        	 * table with bogus crap
					         */
					        if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME)) != NULL)
					                acctsessiontime = pair->lvalue;

						if (acctsessiontime <= 0) {
							radius_xlat(logstr, sizeof(logstr), "stop packet with zero session length. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
							radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s", inst->config->xlat_name, logstr);
							sql_release_socket(inst, sqlsocket);
							ret = RLM_MODULE_NOOP;
						}
#endif

						radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query_alt, request, sql_escape_func);
						query_log(request, inst, querystr);

						if (*querystr) { /* non-empty query */
							if (rlm_sql_query(sqlsocket, inst, querystr)) {
								radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting STOP record - %s",
										inst->config->xlat_name,
										(char *)(inst->module->sql_error)(sqlsocket, inst->config));
								ret = RLM_MODULE_FAIL;
							}
							(inst->module->sql_finish_query)(sqlsocket, inst->config);
						}
					}
				}
				(inst->module->sql_finish_query)(sqlsocket, inst->config);
			}
			break;

			/*
			 *	Anything else is ignored.
			 */
		default:
			radlog(L_INFO, "rlm_sql (%s): Unsupported Acct-Status-Type = %d", inst->config->xlat_name, acctstatustype);
			return RLM_MODULE_NOOP;
			break;

	}

	sql_release_socket(inst, sqlsocket);

	return ret;
}
예제 #4
0
/*
 *	sql xlat function. Right now only SELECTs are supported. Only
 *	the first element of the SELECT result will be used.
 */
static int sql_xlat(void *instance, REQUEST *request,
		    char *fmt, char *out, size_t freespace,
		    RADIUS_ESCAPE_STRING func)
{
	SQLSOCK *sqlsocket;
	SQL_ROW row;
	SQL_INST *inst = instance;
	char querystr[MAX_QUERY_LEN];
	char sqlusername[MAX_STRING_LEN];
	int ret = 0;

	DEBUG("rlm_sql (%s): - sql_xlat", inst->config->xlat_name);
	/*
         * 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, sqlusername, NULL);
	/*
	 * Do an xlat on the provided string (nice recursive operation).
	 */
	if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func)) {
		radlog(L_ERR, "rlm_sql (%s): xlat failed.",
		       inst->config->xlat_name);
		return 0;
	}

	query_log(request, inst,querystr);
	sqlsocket = sql_get_socket(inst);
	if (sqlsocket == NULL)
		return 0;
	if (rlm_sql_select_query(sqlsocket,inst,querystr)){
		radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
		       inst->config->xlat_name,querystr,
		       (char *)(inst->module->sql_error)(sqlsocket, inst->config));
		sql_release_socket(inst,sqlsocket);
		return 0;
	}

	ret = rlm_sql_fetch_row(sqlsocket, inst);

	if (ret) {
		DEBUG("rlm_sql (%s): SQL query did not succeed",
		      inst->config->xlat_name);
		(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
		sql_release_socket(inst,sqlsocket);
		return 0;
	}

	row = sqlsocket->row;
	if (row == NULL) {
		DEBUG("rlm_sql (%s): SQL query did not return any results",
		      inst->config->xlat_name);
		(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
		sql_release_socket(inst,sqlsocket);
		return 0;
	}

	if (row[0] == NULL){
		DEBUG("rlm_sql (%s): row[0] returned NULL",
		      inst->config->xlat_name);
		(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
		sql_release_socket(inst,sqlsocket);
		return 0;
	}
	ret = strlen(row[0]);
	if (ret > freespace){
		DEBUG("rlm_sql (%s): sql_xlat:: Insufficient string space",
		      inst->config->xlat_name);
		(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
		sql_release_socket(inst,sqlsocket);
		return 0;
	}

	strncpy(out,row[0],ret);

	DEBUG("rlm_sql (%s): - sql_xlat finished",
	      inst->config->xlat_name);

	(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
	sql_release_socket(inst,sqlsocket);
	return ret;
}
예제 #5
0
/*
 *	redisn xlat function. Right now only xGET are supported. Only
 *	the first element of the SELECT result will be used.
 */
static int redisn_xlat(void *instance, REQUEST *request,
		    char *fmt, char *out, size_t freespace,
		    UNUSED RADIUS_ESCAPE_STRING func)
{
	REDISSOCK *redis_socket=NULL;
	REDIS_ROW row;
	REDIS_INST *inst = instance;
	char querystr[MAX_QUERY_LEN];
	char redisnusername[MAX_STRING_LEN];
	size_t ret = 0;

	RDEBUG("redisn_xlat");

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

	query_log(request, inst, querystr);
	redis_socket = redisn_get_socket(inst);
	if (redis_socket == NULL)
		return 0;

	if (rlm_redisn_query(inst, redis_socket, querystr)<0) {
	  radlog(L_ERR, "rlm_redisn (%s): database query error, %s",
		 inst->xlat_name,querystr);
	  redisn_release_socket(inst,redis_socket);
	  return 0;
	  }

	ret = rlm_redisn_fetch_row(inst, redis_socket);

	if (ret) {
		RDEBUG("REDIS query did not succeed");
		(inst->redisn_finish_query)(inst, redis_socket);
		redisn_release_socket(inst,redis_socket);
		return 0;
	}

	row = redis_socket->row;
	if (row == NULL) {
		RDEBUG("REDIS query did not return any results");
		(inst->redisn_finish_query)(inst, redis_socket);
		redisn_release_socket(inst,redis_socket);
		return 0;
	}

	if (row[0] == NULL){
		RDEBUG("row[0] returned NULL");
		(inst->redisn_finish_query)(inst, redis_socket);
		redisn_release_socket(inst,redis_socket);
		return 0;
	}
	ret = strlen(row[0]);
	if (ret >= freespace){
		RDEBUG("Insufficient string space");
		(inst->redisn_finish_query)(inst, redis_socket);
		redisn_release_socket(inst,redis_socket);
		return 0;
	}

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

	RDEBUG("redisn_xlat finished");

	(inst->redisn_finish_query)(inst,redis_socket);
	redisn_release_socket(inst,redis_socket);
	return ret;
}
예제 #6
0
/*
 *	Accounting: save the account data to our redisn table
 */
static rlm_rcode_t rlm_redisn_accounting(void *instance, REQUEST * request) {

	REDISSOCK *redis_socket = NULL;
	VALUE_PAIR *pair;
	REDIS_INST *inst = instance;
	int	ret = RLM_MODULE_OK;
	int     acctstatustype = 0;
	char    logstr[MAX_QUERY_LEN];
	char	redisnusername[MAX_STRING_LEN];
	char**  queries=NULL;


	/*
	 * Find the Acct Status Type
	 */
	if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY)) != NULL) {
		acctstatustype = pair->vp_integer;
	} else {
		radius_xlat(logstr, sizeof(logstr), "packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL, inst);
		radlog_request(L_ERR, 0, request, "%s", logstr);
		return RLM_MODULE_INVALID;
	}

	switch (acctstatustype) {
			/*
			 * The Terminal server informed us that it was rebooted
			 * STOP all records from this NAS
			 */
		case PW_STATUS_ACCOUNTING_ON:
		  RDEBUG("Received Acct On packet");
		  queries=inst->accounting_on_queries;
		  break;
		case PW_STATUS_ACCOUNTING_OFF:
		  RDEBUG("Received Acct Off packet");
		  queries=inst->accounting_off_queries;
		  break;
		case PW_STATUS_START:
		  RDEBUG("Received Acct Start packet");
		  queries=inst->accounting_start_queries;
		  break;
		case PW_STATUS_STOP:
		  RDEBUG("Received Acct Stop packet");
#if 0 //#ifdef CISCO_ACCOUNTING_HACK
		  {
		    /*
		     * If stop but zero session length AND no previous
		     * session found, drop it as in invalid packet
		     * This is to fix CISCO's aaa from filling our
		     * table with bogus crap
		   */
		    int     acctsessiontime = 0;
		    if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME, 0, TAG_ANY)) != NULL)
		      acctsessiontime = pair->vp_integer;
		    
		    if (acctsessiontime <= 0) {
		      radius_xlat(logstr, sizeof(logstr), "stop packet with zero session length. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL, inst);
		      radlog_request(L_DBG, 0, request, "%s", logstr);
		      redisn_release_socket(inst, redis_socket);
		      return RLM_MODULE_NOOP;
		    }
		  }
#endif

		  queries=inst->accounting_stop_queries;
		  break;
		case PW_STATUS_ALIVE:
		  RDEBUG("Received Acct Alive packet");
		  queries=inst->accounting_update_queries;
		  break;
		  /*
		   *	Anything else is ignored.
		   */
	        default:
		  RDEBUG("Unsupported Acct-Status-Type = %d",
			 acctstatustype);
		  return RLM_MODULE_NOOP;
		  break;
	}
	if (queries==NULL ||*queries==NULL)
	  ret=RLM_MODULE_NOOP;
	else
	  {
	    char    querystr[MAX_QUERY_LEN];
	    redis_socket = redisn_get_socket(inst);
	    if (redis_socket == NULL)
	      return(RLM_MODULE_FAIL);

	    redisn_set_user(inst, request, redisnusername, NULL);

	    while(*queries) {
	      char* query=*queries;		
	      memset(querystr, 0, MAX_QUERY_LEN);
	      radius_xlat(querystr, sizeof(querystr), query, request, redisn_escape_func, inst);
	      query_log(request, inst, querystr);
	      
	      if (rlm_redisn_query(inst, redis_socket, querystr)) {
		radlog_request(L_ERR, 0, request, "Accounting query failed: %s",
			       querystr);
		ret = RLM_MODULE_FAIL;
	      }

	      (inst->redisn_finish_query)(inst, redis_socket);
	      queries++;
	    }
	    redisn_release_socket(inst, redis_socket);
	  }

	return ret;
}
예제 #7
0
/*
 *	sql xlat function. Right now only SELECTs are supported. Only
 *	the first element of the SELECT result will be used.
 *
 *	For other statements (insert, update, delete, etc.), the
 *	number of affected rows will be returned.
 */
static int sql_xlat(void *instance, REQUEST *request,
		    char *fmt, char *out, size_t freespace,
		    UNUSED RADIUS_ESCAPE_STRING func)
{
	SQLSOCK *sqlsocket;
	SQL_ROW row;
	SQL_INST *inst = instance;
	char querystr[MAX_QUERY_LEN];
	char sqlusername[MAX_STRING_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, sqlusername, NULL);
	/*
	 * Do an xlat on the provided string (nice recursive operation).
	 */
	if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func)) {
		radlog(L_ERR, "rlm_sql (%s): xlat failed.",
		       inst->config->xlat_name);
		return 0;
	}

	query_log(request, inst,querystr);
	sqlsocket = sql_get_socket(inst);
	if (sqlsocket == NULL)
		return 0;

	/*
	 *	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(sqlsocket,inst,querystr)) {
			radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
				inst->config->xlat_name, querystr,
				(inst->module->sql_error)(sqlsocket,
							  inst->config));
			sql_release_socket(inst,sqlsocket);
			return 0;
		}
	       
		numaffected = (inst->module->sql_affected_rows)(sqlsocket,
								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)(sqlsocket,
							 inst->config);
			sql_release_socket(inst,sqlsocket);
			return 0;
		}
		
		memcpy(out, buffer, ret + 1); /* we did bounds checking above */

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

	if (rlm_sql_select_query(sqlsocket,inst,querystr)){
		radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
		       inst->config->xlat_name,querystr,
		       (inst->module->sql_error)(sqlsocket, inst->config));
		sql_release_socket(inst,sqlsocket);
		return 0;
	}

	ret = rlm_sql_fetch_row(sqlsocket, inst);

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

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

	if (row[0] == NULL){
		RDEBUG("row[0] returned NULL");
		(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
		sql_release_socket(inst,sqlsocket);
		return 0;
	}
	ret = strlen(row[0]);
	if (ret >= freespace){
		RDEBUG("Insufficient string space");
		(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
		sql_release_socket(inst,sqlsocket);
		return 0;
	}

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

	RDEBUG("sql_xlat finished");

	(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
	sql_release_socket(inst,sqlsocket);
	return ret;
}