usersfile = ${confdir}/users_fast
hashsize = 1000
compat = no
+ normal_defaults = yes
}
detail {
# Print all SQL statements when in debug mode (-x)
sqltrace = no
+ sqltracefile = ${logdir}/sqltrace.sql
+
+ # number of sql connections to make to server
+ num_sql_socks = 5
}
#
#define PW_ACCT_SESSION_TIME 46
#define PW_ACCT_INPUT_PACKETS 47
#define PW_ACCT_OUTPUT_PACKETS 48
+#define PW_ACCT_TERMINATE_CAUSE 49
#define PW_CHAP_CHALLENGE 60
#define PW_NAS_PORT_TYPE 61
/* SQL defines */
#define SQL_LOCK_LEN sizeof(SQLACCTREC)
-#define SQLQUERYLOG "/var/log/radacct/radius.sql"
-#define SQLCONFIGFILE "rlm_sql.conf"
-#define SQLBACKUP "/var/log/radacct/sqlbackup.dat"
+#define SQLTRACEFILE RADLOG_DIR "/sqltrace.sql"
#define SQLBIGREC 32
#define SQLLILREC 15
#define MAX_COMMUNITY_LEN 50
-#define MAX_SQL_SOCKS 5
+#define MAX_SQL_SOCKS 256
#define MAX_TABLE_LEN 20
#define MAX_AUTH_QUERY_LEN 256
#define AUTH_STRING_LEN 128
CREATE TABLE radacct (
RadAcctId bigint(21) DEFAULT '0' NOT NULL auto_increment,
AcctSessionId varchar(32) DEFAULT '' NOT NULL,
+ AcctUniqueId varchar(32) DEFAULT '',
UserName varchar(32) DEFAULT '' NOT NULL,
Realm varchar(30) DEFAULT '',
NASIPAddress varchar(15) DEFAULT '' NOT NULL,
AcctStopTime datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
AcctSessionTime int(12),
AcctAuthentic varchar(32),
- ConnectInfo varchar(32),
+ ConnectInfo_start varchar(32),
+ ConnectInfo_stop varchar(32),
AcctInputOctets int(12),
AcctOutputOctets int(12),
CalledStationId varchar(10) DEFAULT '' NOT NULL,
KEY UserName (UserName),
KEY FramedIPAddress (FramedIPAddress),
KEY AcctSessionId (AcctSessionId),
+ KEY AcctUniqueId (AcctUniqueId),
KEY AcctStartTime (AcctStartTime),
KEY AcctStopTime (AcctStopTime),
KEY NASIPAddress (NASIPAddress)
* *
* Mike Machado <mike@innercite.com> *
***************************************************************************/
-static const char rcsid[] = "$Id$";
+static const char rcsid[] =
+
+ "$Id$";
#include "autoconf.h"
#include <netinet/in.h>
#include <arpa/inet.h>
-static SQL_CONFIG config = {
- NULL, /* "localhost" */
- NULL, /* "root" */
- NULL, /* "" */
- NULL, /* "radius" */
- NULL, /* "radacct" */
- NULL, /* "radcheck" */
- NULL, /* "radreply" */
- NULL, /* "radgroupcheck" */
- NULL, /* "radgroupreply" */
- NULL, /* "usergroup" */
- NULL, /* "realm" */
- NULL, /* "realmgroup" */
- NULL, /* "nas" */
- NULL, /* "dictionary" */
- 0,
- 0,
- 1,
- 5
-};
+static SQL_CONFIG config;
static CONF_PARSER module_config[] = {
- { "sensitiveusername", PW_TYPE_BOOLEAN,
- &config.sensitiveusername, "1" },
- { "deletestalesessions", PW_TYPE_BOOLEAN,
- &config.deletestalesessions, "0" },
- { "sqltrace", PW_TYPE_BOOLEAN,
- &config.sqltrace, "0" },
- { "max_sql_socks", PW_TYPE_INTEGER,
- &config.max_sql_socks, Stringify(MAX_SQL_SOCKS) },
- { "server", PW_TYPE_STRING_PTR,
- &config.sql_server, "localhost" },
- { "login", PW_TYPE_STRING_PTR,
- &config.sql_login, "" },
- { "password", PW_TYPE_STRING_PTR,
- &config.sql_password, "" },
- { "db", PW_TYPE_STRING_PTR,
- &config.sql_db, "radius" },
- { "authcheck_table", PW_TYPE_STRING_PTR,
- &config.sql_authcheck_table, "radcheck" },
- { "authreply_table", PW_TYPE_STRING_PTR,
- &config.sql_authreply_table, "radreply" },
- { "groupcheck_table", PW_TYPE_STRING_PTR,
- &config.sql_groupcheck_table, "radgroupcheck" },
- { "groupreply_table", PW_TYPE_STRING_PTR,
- &config.sql_groupreply_table, "radgroupreply" },
- { "usergroup_table", PW_TYPE_STRING_PTR,
- &config.sql_usergroup_table, "usergroup" },
- { "realmgroup_table", PW_TYPE_STRING_PTR,
- &config.sql_realmgroup_table, "realmgroup" },
- { "acct_table", PW_TYPE_STRING_PTR,
- &config.sql_acct_table, "radacct" },
- { "nas_table", PW_TYPE_STRING_PTR,
- &config.sql_nas_table, "nas" },
- { "realm_table", PW_TYPE_STRING_PTR,
- &config.sql_realm_table, "realms" },
- { "dict_table", PW_TYPE_STRING_PTR,
- &config.sql_dict_table, "dictionary" },
- { NULL, -1, NULL, NULL }
+ {"server", PW_TYPE_STRING_PTR,
+ &config.sql_server, "localhost"},
+ {"login", PW_TYPE_STRING_PTR,
+ &config.sql_login, ""},
+ {"password", PW_TYPE_STRING_PTR,
+ &config.sql_password, ""},
+ {"radius_db", PW_TYPE_STRING_PTR,
+ &config.sql_db, "radius"},
+ {"acct_table", PW_TYPE_STRING_PTR,
+ &config.sql_acct_table, "radacct"},
+ {"authcheck_table", PW_TYPE_STRING_PTR,
+ &config.sql_authcheck_table, "radcheck"},
+ {"authreply_table", PW_TYPE_STRING_PTR,
+ &config.sql_authreply_table, "radreply"},
+ {"groupcheck_table", PW_TYPE_STRING_PTR,
+ &config.sql_groupcheck_table, "radgroupcheck"},
+ {"groupreply_table", PW_TYPE_STRING_PTR,
+ &config.sql_groupreply_table, "radgroupreply"},
+ {"usergroup_table", PW_TYPE_STRING_PTR,
+ &config.sql_usergroup_table, "usergroup"},
+ {"realm_table", PW_TYPE_STRING_PTR,
+ &config.sql_realm_table, "realms"},
+ {"realmgroup_table", PW_TYPE_STRING_PTR,
+ &config.sql_realmgroup_table, "realmgroup"},
+ {"nas_table", PW_TYPE_STRING_PTR,
+ &config.sql_nas_table, "nas"},
+ {"dict_table", PW_TYPE_STRING_PTR,
+ &config.sql_dict_table, "dictionary"},
+ {"sensitiveusername", PW_TYPE_BOOLEAN,
+ &config.sensitiveusername, "1"},
+ {"sqltrace", PW_TYPE_BOOLEAN,
+ &config.sqltrace, "0"},
+ {"sqltracefile", PW_TYPE_STRING_PTR,
+ &config.tracefile, SQLTRACEFILE},
+ {"deletestalesessions", PW_TYPE_BOOLEAN,
+ &config.deletestalesessions, "0"},
+ {"num_sql_socks", PW_TYPE_INTEGER,
+ &config.num_sql_socks, "5"},
+ {NULL, -1, NULL, NULL}
};
-
/***********************************************************************
* start of main routines
***********************************************************************/
-static int rlm_sql_init(void) {
+static int
+rlm_sql_init(void)
+{
+ /*
+ * FIXME:
+ * We should put the socket array here once
+ * the module code is reworked to not unload
+ * modules on HUP. This way we can have
+ * persistant connections. -jcarneal
+ */
+ return 0;
+}
+
+static int
+rlm_sql_instantiate(CONF_SECTION *conf, void **instance)
+{
+ SQL_INST *inst;
- /* Where is the flag that tells us about a HUP?*/
- int reload = 0;
+ if ((inst = malloc(sizeof(SQL_INST))) == NULL) {
+ radlog(L_ERR | L_CONS, "sql_instantiate: no memory");
+ return -1;
+ }
+ memset(inst, 0, sizeof(SQL_INST));
+ if ((inst->config = malloc(sizeof(SQL_CONFIG))) == NULL) {
+ radlog(L_ERR | L_CONS, "sql_instantiate: no memory");
+ free(inst);
+ return -1;
+ }
+ memset(inst->config, 0, sizeof(SQL_CONFIG));
+
+ /*
+ * If the configuration parameters can't be parsed, then
+ * fail.
+ */
+ if (cf_section_parse(conf, module_config) < 0) {
+ free(inst->config);
+ free(inst);
+ return -1;
+ }
+
+ if(config.num_sql_socks > MAX_SQL_SOCKS) {
+ radlog(L_ERR | L_CONS, "sql_instantiate: number of sockets cannot exceed %d",
+ MAX_SQL_SOCKS);
+ free(inst->config);
+ free(inst);
+ return -1;
+ }
- if ((sql = malloc(sizeof(SQL))) == NULL) {
- radlog(L_ERR|L_CONS, "no memory");
- exit(1);
+ inst->config->sql_server = config.sql_server;
+ inst->config->sql_login = config.sql_login;
+ inst->config->sql_password = config.sql_password;
+ inst->config->sql_db = config.sql_db;
+ inst->config->sql_acct_table = config.sql_acct_table;
+ inst->config->sql_authcheck_table = config.sql_authcheck_table;
+ inst->config->sql_authreply_table = config.sql_authreply_table;
+ inst->config->sql_groupcheck_table = config.sql_groupcheck_table;
+ inst->config->sql_groupreply_table = config.sql_groupreply_table;
+ inst->config->sql_usergroup_table = config.sql_usergroup_table;
+ inst->config->sql_realm_table = config.sql_realm_table;
+ inst->config->sql_realmgroup_table = config.sql_realmgroup_table;
+ inst->config->sql_nas_table = config.sql_nas_table;
+ inst->config->sql_dict_table = config.sql_dict_table;
+ inst->config->sensitiveusername = config.sensitiveusername;
+ inst->config->sqltrace = config.sqltrace;
+ inst->config->tracefile = config.tracefile;
+ inst->config->deletestalesessions = config.deletestalesessions;
+ inst->config->num_sql_socks = config.num_sql_socks;
+
+ config.sql_server = NULL;
+ config.sql_login = NULL;
+ config.sql_password = NULL;
+ config.sql_db = NULL;
+ config.sql_acct_table = NULL;
+ config.sql_authcheck_table = NULL;
+ config.sql_authreply_table = NULL;
+ config.sql_groupcheck_table = NULL;
+ config.sql_groupreply_table = NULL;
+ config.sql_usergroup_table = NULL;
+ config.sql_realm_table = NULL;
+ config.sql_realmgroup_table = NULL;
+ config.sql_nas_table = NULL;
+ config.sql_dict_table = NULL;
+ config.tracefile = NULL;
+
+ radlog(L_INFO, "rlm_sql: Attempting to connect to %s@%s:%s",
+ inst->config->sql_login, inst->config->sql_server,
+ inst->config->sql_db);
+
+#if HAVE_PTHREAD_H
+ pthread_mutex_init(&inst->sqlsock_mutex, NULL);
+#endif
+
+ if(sql_init_socket(inst) < 0) {
+ free(inst->config);
+ free(inst);
+ return -1;
}
-/*
- if (reload)
- free(sql->config);
- if ((sql->config = malloc(sizeof(SQL_CONFIG))) == NULL) {
- radlog(L_ERR|L_CONS, "no memory");
- exit(1);
- }
-*/
+ *instance = inst;
+
+ return RLM_MODULE_OK;
+}
- sql_init(module_config, &config, reload);
+static int
+rlm_sql_destroy(void)
+{
- return 0;
+ return 0;
}
-static int rlm_sql_destroy(void) {
+static int
+rlm_sql_detach(void *instance)
+{
+ int i;
+ SQL_INST *inst = instance;
+
+ /*
+ * Close up all our sql connections
+ */
+ for (i = 0; i < inst->config->num_sql_socks; i++)
+ if(inst->socks[i])
+ if (!sql_close_socket(inst->socks[i]))
+ radlog(L_CONS | L_ERR, "rlm_sql: Could not release socket %d", i);
+
+ free(inst->config);
+ free(inst);
+
+#if HAVE_PTHREAD_H
+ pthread_mutex_destroy(&inst->sqlsock_mutex);
+#endif
- return 0;
+ return 0;
}
-static int rlm_sql_authorize(REQUEST *request)
+static int
+rlm_sql_authorize(void *instance, REQUEST * request)
{
- int nas_port = 0;
- VALUE_PAIR *check_tmp = NULL;
- VALUE_PAIR *reply_tmp = NULL;
- VALUE_PAIR *tmp;
- int found = 0;
- char *name;
- SQLSOCK *socket;
-
+ int nas_port = 0;
+ VALUE_PAIR *check_tmp = NULL;
+ VALUE_PAIR *reply_tmp = NULL;
+ VALUE_PAIR *tmp;
+ int found = 0;
+ char *name;
+ SQLSOCK *socket;
+ SQL_INST *inst = instance;
+
name = request->username->strvalue;
- /*
- * Check for valid input, zero length names not permitted
- */
- if (name[0] == 0) {
- radlog(L_ERR, "zero length username not permitted\n");
- return -1;
- }
-
- socket = sql_get_socket();
-
- /*
- * Find the NAS port ID.
- */
- if ((tmp = pairfind(request->packet->vps, PW_NAS_PORT_ID)) != NULL)
- nas_port = tmp->lvalue;
-
- /*
- * Find the entry for the user.
- */
- if ((found = sql_getvpdata(socket, sql->config->sql_authcheck_table, &check_tmp, name, PW_VP_USERDATA)) > 0) {
- sql_getvpdata(socket, sql->config->sql_groupcheck_table, &check_tmp, name, PW_VP_GROUPDATA);
- sql_getvpdata(socket, sql->config->sql_authreply_table, &reply_tmp, name, PW_VP_USERDATA);
- sql_getvpdata(socket, sql->config->sql_groupreply_table, &reply_tmp, name, PW_VP_GROUPDATA);
- } else {
-
- int gcheck, greply;
- gcheck = sql_getvpdata(socket, sql->config->sql_groupcheck_table, &check_tmp, "DEFAULT", PW_VP_GROUPDATA);
- greply = sql_getvpdata(socket, sql->config->sql_groupreply_table, &reply_tmp, "DEFAULT", PW_VP_GROUPDATA);
- if (gcheck && greply)
- found = 1;
- }
- sql_release_socket(socket);
-
- if (!found) {
- DEBUG2("User %s not found and DEFAULT not found", name);
- return RLM_MODULE_NOTFOUND;
- }
-
- if (paircmp(request->packet->vps, check_tmp, &reply_tmp) != 0) {
- DEBUG2("Pairs do not match [%s]", name);
- return RLM_MODULE_OK;
- }
-
- pairmove(&request->reply->vps, &reply_tmp);
- pairmove(&request->config_items, &check_tmp);
- pairfree(reply_tmp);
- pairfree(check_tmp);
-
-
+ /*
+ * Check for valid input, zero length names not permitted
+ */
+ if (name[0] == 0) {
+ radlog(L_ERR, "zero length username not permitted\n");
+ return -1;
+ }
+
+ socket = sql_get_socket(inst);
+
+ /*
+ * Find the NAS port ID.
+ */
+ if ((tmp = pairfind(request->packet->vps, PW_NAS_PORT_ID)) != NULL)
+ nas_port = tmp->lvalue;
+
+ /*
+ * Find the entry for the user.
+ */
+ if ((found = sql_getvpdata(inst, socket, inst->config->sql_authcheck_table,
+ &check_tmp, name, PW_VP_USERDATA)) > 0) {
+ sql_getvpdata(inst, socket, inst->config->sql_groupcheck_table, &check_tmp,
+ name, PW_VP_GROUPDATA);
+ sql_getvpdata(inst, socket, inst->config->sql_authreply_table, &reply_tmp,
+ name, PW_VP_USERDATA);
+ sql_getvpdata(inst, socket, inst->config->sql_groupreply_table, &reply_tmp,
+ name, PW_VP_GROUPDATA);
+ } else {
+
+ int gcheck, greply;
+
+ gcheck = sql_getvpdata(inst, socket, inst->config->sql_groupcheck_table,
+ &check_tmp, "DEFAULT", PW_VP_GROUPDATA);
+ greply = sql_getvpdata(inst, socket, inst->config->sql_groupreply_table,
+ &reply_tmp, "DEFAULT", PW_VP_GROUPDATA);
+ if (gcheck && greply)
+ found = 1;
+ }
+ sql_release_socket(inst, socket);
+
+ if (!found) {
+ DEBUG2("rlm_sql: User %s not found and DEFAULT not found", name);
+ return RLM_MODULE_NOTFOUND;
+ }
+
+ if (paircmp(request->packet->vps, check_tmp, &reply_tmp) != 0) {
+ DEBUG2("rlm_sql: Pairs do not match [%s]", name);
+ return RLM_MODULE_OK;
+ }
+
+ pairmove(&request->reply->vps, &reply_tmp);
+ pairmove(&request->config_items, &check_tmp);
+ pairfree(reply_tmp);
+ pairfree(check_tmp);
+
return RLM_MODULE_OK;
}
-static int rlm_sql_authenticate(REQUEST *request)
+static int
+rlm_sql_authenticate(void *instance, REQUEST *request)
{
-
- SQL_ROW row;
- SQLSOCK *socket;
- char *querystr;
- char escaped_user[AUTH_STRING_LEN*3];
- char *user;
- const char query[] = "SELECT Value FROM %s WHERE UserName = '%s' AND Attribute = 'Password'";
-
+
+ SQL_ROW row;
+ SQLSOCK *socket;
+ char *querystr;
+ char escaped_user[AUTH_STRING_LEN * 3];
+ char *user;
+ const char query[] = "SELECT Value FROM %s WHERE UserName = '%s' AND Attribute = 'Password'";
+ SQL_INST *inst = instance;
+
user = request->username->strvalue;
-
+
/*
- * Ensure that a password attribute exists.
+ * Ensure that a password attribute exists.
*/
if ((request->password == NULL) ||
- (request->password->length == 0) ||
- (request->password->attribute != PW_PASSWORD)) {
- radlog(L_AUTH, "rlm_sql: Attribute \"Password\" is required for authentication.");
+ (request->password->length == 0) ||
+ (request->password->attribute != PW_PASSWORD)) {
+ radlog(L_AUTH,
+ "rlm_sql: Attribute \"Password\" is required for authentication.");
return RLM_MODULE_INVALID;
}
-
+
sql_escape_string(escaped_user, user, strlen(user));
-
+
/*
- * This should really be replaced with a static buffer...
+ * This should really be replaced with a static buffer...
*/
if ((querystr = malloc(strlen(escaped_user) +
- strlen(sql->config->sql_authcheck_table) +
- sizeof(query))) == NULL) {
- radlog(L_ERR|L_CONS, "no memory");
- exit(1);
- }
-
- sprintf(querystr, query, sql->config->sql_authcheck_table, escaped_user);
- socket = sql_get_socket();
- sql_select_query(socket, querystr);
+ strlen(inst->config->sql_authcheck_table) +
+ sizeof(query))) == NULL) {
+ radlog(L_ERR | L_CONS, "no memory");
+ exit(1);
+ }
+
+ sprintf(querystr, query, inst->config->sql_authcheck_table, escaped_user);
+ socket = sql_get_socket(inst);
+ sql_select_query(inst, socket, querystr);
row = sql_fetch_row(socket);
sql_finish_select_query(socket);
- free(querystr);
-
- if (strncmp(request->password->strvalue, row[0], request->password->length) != 0)
+ free(querystr);
+
+ if (strncmp(request->password->strvalue, row[0], request->password->length)
+ != 0)
return RLM_MODULE_REJECT;
else
return RLM_MODULE_OK;
}
/*
- * Accounting: does nothing for now.
+ * Accounting: save the account data to our sql table
*/
-static int rlm_sql_accounting(REQUEST *request) {
-
- time_t nowtime;
- struct tm *tim;
- char datebuf[20];
- VALUE_PAIR *pair;
- SQLACCTREC *sqlrecord;
- SQLSOCK *socket;
- DICT_VALUE *dval;
-
-
- if ((sqlrecord = malloc(sizeof(SQLACCTREC))) == NULL) {
- radlog(L_ERR|L_CONS, "no memory");
- exit(1);
- }
-
- pair = request->packet->vps;
- while(pair != (VALUE_PAIR *)NULL) {
-
- /* Check the pairs to see if they are anything we are interested in. */
- switch(pair->attribute) {
- case PW_ACCT_SESSION_ID:
- strncpy(sqlrecord->AcctSessionId, pair->strvalue, SQLBIGREC);
- break;
-
- case PW_USER_NAME:
- strncpy(sqlrecord->UserName, pair->strvalue, SQLBIGREC);
- break;
-
- case PW_NAS_IP_ADDRESS:
- ip_ntoa(sqlrecord->NASIPAddress, pair->lvalue);
- //ipaddr2str(sqlrecord->NASIPAddress, pair->lvalue);
- break;
-
- case PW_NAS_PORT_ID:
- sqlrecord->NASPortId = pair->lvalue;
- break;
-
- case PW_NAS_PORT_TYPE:
- dval = dict_valbyattr(PW_NAS_PORT_TYPE, pair->lvalue);
- if(dval != NULL) {
- strncpy(sqlrecord->NASPortType, dval->attrname, SQLBIGREC);
- }
- break;
-
- case PW_ACCT_STATUS_TYPE:
- sqlrecord->AcctStatusTypeId = pair->lvalue;
- dval = dict_valbyattr(PW_ACCT_STATUS_TYPE, pair->lvalue);
- if(dval != NULL) {
- strncpy(sqlrecord->AcctStatusType, dval->attrname, SQLBIGREC);
- }
- break;
-
- case PW_ACCT_SESSION_TIME:
- sqlrecord->AcctSessionTime = pair->lvalue;
- break;
-
- case PW_ACCT_AUTHENTIC:
- dval = dict_valbyattr(PW_ACCT_AUTHENTIC, pair->lvalue);
- if(dval != NULL) {
- strncpy(sqlrecord->AcctAuthentic, dval->attrname, SQLBIGREC);
- }
- break;
-
- case PW_CONNECT_INFO:
- strncpy(sqlrecord->ConnectInfo, pair->strvalue, SQLBIGREC);
- break;
-
- case PW_ACCT_INPUT_OCTETS:
- sqlrecord->AcctInputOctets = pair->lvalue;
- break;
-
- case PW_ACCT_OUTPUT_OCTETS:
- sqlrecord->AcctOutputOctets = pair->lvalue;
- break;
-
- case PW_CALLED_STATION_ID:
- strncpy(sqlrecord->CalledStationId, pair->strvalue, SQLLILREC);
- break;
-
- case PW_CALLING_STATION_ID:
- strncpy(sqlrecord->CallingStationId, pair->strvalue, SQLLILREC);
- break;
-
-/* case PW_ACCT_TERMINATE_CAUSE:
- dval = dict_valbyattr(PW_ACCT_TERMINATE_CAUSE, pair->lvalue);
- if(dval != NULL) {
- strncpy(sqlrecord->AcctTerminateCause, dval->attrname, SQLBIGREC);
- }
- break;
-*/
-
-
- case PW_SERVICE_TYPE:
- dval = dict_valbyattr(PW_SERVICE_TYPE, pair->lvalue);
- if(dval != NULL) {
- strncpy(sqlrecord->ServiceType, dval->attrname, SQLBIGREC);
- }
- break;
-
- case PW_FRAMED_PROTOCOL:
- dval = dict_valbyattr(PW_FRAMED_PROTOCOL, pair->lvalue);
- if(dval != NULL) {
- strncpy(sqlrecord->FramedProtocol, dval->attrname, SQLBIGREC);
- }
- break;
-
- case PW_FRAMED_IP_ADDRESS:
- ip_ntoa(sqlrecord->FramedIPAddress, pair->lvalue);
- //ipaddr2str(sqlrecord->FramedIPAddress, pair->lvalue);
- break;
-
- case PW_ACCT_DELAY_TIME:
- sqlrecord->AcctDelayTime = pair->lvalue;
- break;
-
- default:
- break;
- }
-
- pair = pair->next;
- }
-
-
- nowtime = request->timestamp - sqlrecord->AcctDelayTime;
- tim = localtime(&nowtime);
- strftime(datebuf, sizeof(datebuf), "%Y%m%d%H%M%S", tim);
-
- strncpy(sqlrecord->AcctTimeStamp, datebuf, 20);
-
-
- socket = sql_get_socket();
- if (sql_save_acct(socket, sqlrecord) == 0)
- return RLM_MODULE_FAIL;
- sql_release_socket(socket);
+static int
+rlm_sql_accounting(void *instance, REQUEST * request)
+{
+
+ time_t nowtime;
+ struct tm *tim;
+ char datebuf[20];
+ VALUE_PAIR *pair;
+ SQLACCTREC *sqlrecord;
+ SQLSOCK *socket;
+ DICT_VALUE *dval;
+ SQL_INST *inst = instance;
+ int lentmp = 0;
+
+ /*
+ * FIXME: Should we really do this malloc?
+ * Why not a static structure, because this malloc is
+ * relatively expensive considering we do it for every
+ * accounting packet
+ */
+ if ((sqlrecord = malloc(sizeof(SQLACCTREC))) == NULL) {
+ radlog(L_ERR | L_CONS, "no memory");
+ exit(1);
+ }
+ memset(sqlrecord, 0, sizeof(SQLACCTREC));
+
+ pair = request->packet->vps;
+ while (pair != (VALUE_PAIR *) NULL) {
+
+ /*
+ * Check the pairs to see if they are anything we are interested in.
+ */
+ switch (pair->attribute) {
+ case PW_ACCT_SESSION_ID:
+ strncpy(sqlrecord->AcctSessionId, pair->strvalue, SQLBIGREC);
+ break;
+
+ case PW_ACCT_UNIQUE_SESSION_ID:
+ strncpy(sqlrecord->AcctUniqueId, pair->strvalue, SQLBIGREC);
+ break;
+
+ case PW_USER_NAME:
+ strncpy(sqlrecord->UserName, pair->strvalue, SQLBIGREC);
+ break;
+
+ case PW_NAS_IP_ADDRESS:
+ ip_ntoa(sqlrecord->NASIPAddress, pair->lvalue);
+ //ipaddr2str(sqlrecord->NASIPAddress, pair->lvalue);
+ break;
+
+ case PW_NAS_PORT_ID:
+ sqlrecord->NASPortId = pair->lvalue;
+ break;
+
+ case PW_NAS_PORT_TYPE:
+ dval = dict_valbyattr(PW_NAS_PORT_TYPE, pair->lvalue);
+ if (dval != NULL) {
+ strncpy(sqlrecord->NASPortType, dval->attrname, SQLBIGREC);
+ }
+ break;
+
+ case PW_ACCT_STATUS_TYPE:
+ sqlrecord->AcctStatusTypeId = pair->lvalue;
+ dval = dict_valbyattr(PW_ACCT_STATUS_TYPE, pair->lvalue);
+ if (dval != NULL) {
+ strncpy(sqlrecord->AcctStatusType, dval->attrname, SQLBIGREC);
+ }
+ break;
+
+ case PW_ACCT_SESSION_TIME:
+ sqlrecord->AcctSessionTime = pair->lvalue;
+ break;
+
+ case PW_ACCT_AUTHENTIC:
+ dval = dict_valbyattr(PW_ACCT_AUTHENTIC, pair->lvalue);
+ if (dval != NULL) {
+ strncpy(sqlrecord->AcctAuthentic, dval->attrname, SQLBIGREC);
+ }
+ break;
+
+ case PW_CONNECT_INFO:
+ strncpy(sqlrecord->ConnectInfo, pair->strvalue, SQLBIGREC);
+ break;
+
+ case PW_ACCT_INPUT_OCTETS:
+ sqlrecord->AcctInputOctets = pair->lvalue;
+ break;
+
+ case PW_ACCT_OUTPUT_OCTETS:
+ sqlrecord->AcctOutputOctets = pair->lvalue;
+ break;
+
+ case PW_CALLED_STATION_ID:
+ strncpy(sqlrecord->CalledStationId, pair->strvalue, SQLLILREC);
+ break;
+
+ case PW_CALLING_STATION_ID:
+ /* USR 00 workaround */
+ lentmp = strlen(pair->strvalue);
+ if(lentmp > 10) {
+ strncpy(sqlrecord->CallingStationId, pair->strvalue+(lentmp-10), SQLLILREC);
+ } else {
+ strncpy(sqlrecord->CallingStationId, pair->strvalue, SQLLILREC);
+ }
+ break;
+
+ case PW_ACCT_TERMINATE_CAUSE:
+ dval = dict_valbyattr(PW_ACCT_TERMINATE_CAUSE, pair->lvalue);
+ if(dval != NULL) {
+ strncpy(sqlrecord->AcctTerminateCause, dval->attrname, SQLBIGREC);
+ }
+ break;
+
+ case PW_SERVICE_TYPE:
+ dval = dict_valbyattr(PW_SERVICE_TYPE, pair->lvalue);
+ if (dval != NULL) {
+ strncpy(sqlrecord->ServiceType, dval->attrname, SQLBIGREC);
+ }
+ break;
+
+ case PW_FRAMED_PROTOCOL:
+ dval = dict_valbyattr(PW_FRAMED_PROTOCOL, pair->lvalue);
+ if (dval != NULL) {
+ strncpy(sqlrecord->FramedProtocol, dval->attrname, SQLBIGREC);
+ }
+ break;
+
+ case PW_FRAMED_IP_ADDRESS:
+ ip_ntoa(sqlrecord->FramedIPAddress, pair->lvalue);
+ //ipaddr2str(sqlrecord->FramedIPAddress, pair->lvalue);
+ break;
+
+ case PW_ACCT_DELAY_TIME:
+ sqlrecord->AcctDelayTime = pair->lvalue;
+ break;
+
+ /*
+ * FIXME: USR VSA for: USR-Connect-Speed
+ * Ugly hack. Will go away when conf-based
+ * tables are implemented
+ */
+ case 167971:
+ dval = dict_valbyattr(167971, pair->lvalue);
+ if(dval != NULL)
+ strncpy(sqlrecord->ConnectInfo, dval->attrname, SQLBIGREC);
+ break;
+
+ /* Appears to be LE-Terminate-Detail */
+ case 65538:
+ strncpy(sqlrecord->AcctTerminateCause, pair->strvalue, SQLBIGREC);
+ break;
+
+ default:
+ break;
+ }
+
+ pair = pair->next;
+ }
+
+
+ nowtime = request->timestamp - sqlrecord->AcctDelayTime;
+ tim = localtime(&nowtime);
+ strftime(datebuf, sizeof(datebuf), "%Y%m%d%H%M%S", tim);
+
+ strncpy(sqlrecord->AcctTimeStamp, datebuf, 20);
+
+ socket = sql_get_socket(inst);
+ sql_save_acct(inst, socket, sqlrecord);
+ sql_release_socket(inst, socket);
return RLM_MODULE_OK;
}
/* globally exported name */
module_t rlm_sql = {
- "SQL",
- 0, /* type: reserved */
- rlm_sql_init, /* initialization */
- NULL, /* instantiation */
- rlm_sql_authorize, /* authorization */
- rlm_sql_authenticate, /* authentication */
- NULL, /* preaccounting */
- rlm_sql_accounting, /* accounting */
- NULL, /* checksimul */
- NULL, /* detach */
- rlm_sql_destroy, /* destroy */
+ "SQL",
+ 0, /* type: reserved */
+ rlm_sql_init, /* initialization */
+ rlm_sql_instantiate, /* instantiation */
+ rlm_sql_authorize, /* authorization */
+ rlm_sql_authenticate, /* authentication */
+ NULL, /* preaccounting */
+ rlm_sql_accounting, /* accounting */
+ NULL, /* checksimul */
+ rlm_sql_detach, /* detach */
+ rlm_sql_destroy, /* destroy */
};
+
/***************************************************************************
* rlm_sql.h rlm_sql - FreeRADIUS SQL Module *
* *
#define PW_VP_REALMDATA 3
typedef struct sqlrec {
- char AcctSessionId[SQLBIGREC];
- char UserName[SQLBIGREC];
- char Realm[SQLBIGREC];
- char NASIPAddress[SQLLILREC];
- unsigned long NASPortId;
- char NASPortType[SQLBIGREC];
- char AcctStatusType[SQLBIGREC];
- unsigned int AcctStatusTypeId;
- char AcctTimeStamp[20];
- unsigned long AcctSessionTime;
- char AcctAuthentic[SQLBIGREC];
- char ConnectInfo[SQLBIGREC];
- unsigned long AcctInputOctets;
- unsigned long AcctOutputOctets;
- char CalledStationId[SQLLILREC];
- char CallingStationId[SQLLILREC];
- char AcctTerminateCause[SQLBIGREC];
- char ServiceType[SQLBIGREC];
- char FramedProtocol[SQLBIGREC];
- char FramedIPAddress[SQLLILREC];
- unsigned long AcctDelayTime;
-} SQLACCTREC;
-
-int sql_init(CONF_PARSER *module_config, SQL_CONFIG *config, int reload);
-int sql_init_socket(int reload);
-int sql_close_socket(SQLSOCK *socket);
-SQLSOCK *sql_get_socket(void);
-int sql_release_socket(SQLSOCK *socket);
-int sql_save_acct(SQLSOCK *socket, SQLACCTREC *sqlrecord);
-int sql_userparse(VALUE_PAIR **first_pair, SQL_ROW row, int mode);
-int sql_read_realms(SQLSOCK *socket);
-int sql_getvpdata(SQLSOCK *socket, char *table, VALUE_PAIR **vp, char *user, int mode);
-int sql_check_multi(SQLSOCK *socket, char *name, VALUE_PAIR *request, int maxsimul);
-int sql_read_naslist(SQLSOCK *socket);
-int sql_read_clients(SQLSOCK *socket);
-int sql_dict_init(SQLSOCK *socket);
+ char AcctSessionId[SQLBIGREC];
+ char AcctUniqueId[SQLBIGREC];
+ char UserName[SQLBIGREC];
+ char Realm[SQLBIGREC];
+ char NASIPAddress[SQLLILREC];
+ unsigned long NASPortId;
+ char NASPortType[SQLBIGREC];
+ char AcctStatusType[SQLBIGREC];
+ unsigned int AcctStatusTypeId;
+ char AcctTimeStamp[20];
+ unsigned long AcctSessionTime;
+ char AcctAuthentic[SQLBIGREC];
+ char ConnectInfo[SQLBIGREC];
+ unsigned long AcctInputOctets;
+ unsigned long AcctOutputOctets;
+ char CalledStationId[SQLLILREC];
+ char CallingStationId[SQLLILREC];
+ char AcctTerminateCause[SQLBIGREC];
+ char ServiceType[SQLBIGREC];
+ char FramedProtocol[SQLBIGREC];
+ char FramedIPAddress[SQLLILREC];
+ unsigned long AcctDelayTime;
+} SQLACCTREC;
-SQL *sql;
+int sql_init_socket(SQL_INST *inst);
+int sql_close_socket(SQLSOCK * socket);
+SQLSOCK *sql_get_socket(SQL_INST *inst);
+int sql_release_socket(SQL_INST *inst, SQLSOCK * socket);
+int sql_save_acct(SQL_INST *inst, SQLSOCK * socket, SQLACCTREC * sqlrecord);
+int sql_userparse(VALUE_PAIR ** first_pair, SQL_ROW row, int mode);
+int sql_read_realms(SQLSOCK * socket);
+int sql_getvpdata(SQL_INST *inst, SQLSOCK * socket, char *table, VALUE_PAIR ** vp,
+ char *user, int mode);
+int sql_check_multi(SQL_INST *inst, SQLSOCK * socket, char *name,
+ VALUE_PAIR * request, int maxsimul);
+int sql_read_naslist(SQLSOCK * socket);
+int sql_read_clients(SQLSOCK * socket);
+int sql_dict_init(SQLSOCK * socket);
/*************************************************************************
*
- * Function: mysql_start
- *
- * Purpose: Reads SQL Config File
- *
- *************************************************************************/
-
-int sql_init(CONF_PARSER *module_config, SQL_CONFIG *config, int reload) {
-
- CONF_SECTION *sql_cs;
-
- /*
- * Initialize the data structure with dynamically allocated
- * memory.
- */
- config->sql_server = strdup("localhost");
- config->sql_login = strdup("");
- config->sql_password = strdup("");
- config->sql_db = strdup("radius");
- config->sql_authcheck_table = strdup("radcheck");
- config->sql_authreply_table = strdup("radreply");
- config->sql_groupcheck_table = strdup("radgroupcheck");
- config->sql_groupreply_table = strdup("radgroupreply");
- config->sql_usergroup_table = strdup("usergroup");
- config->sql_realmgroup_table = strdup("realmgroup");
- config->sql_acct_table = strdup("radacct");
- config->sqltrace = 0;
- config->sensitiveusername = 1;
- config->deletestalesessions = 0;
-
- config->sql_nas_table = strdup("nas");
- config->sql_realm_table = strdup( "realms");
- config->sql_dict_table = strdup("dictionary");
- config->max_sql_socks = MAX_SQL_SOCKS;
-
-
- /*
- * Look for the module's configuration. If it doesn't
- * exists, exit quietly (and use the defaults).
- */
- sql_cs = cf_module_config_find("sql");
- if (!sql_cs) {
- return 0;
- }
-
- /*
- * If the configuration parameters can't be parsed, then
- * fail.
- */
- if (cf_section_parse(sql_cs, module_config) < 0) {
- return -1;
- }
-
- sql->config = config;
-
- radlog(L_INFO,"SQL: Attempting to connect to %s@%s:%s", sql->config->sql_login, sql->config->sql_server, sql->config->sql_db);
-
- sql_init_socket(reload);
-
- return 0;
-}
-
-
-/*************************************************************************
- *
* Function: sql_init_socket
*
* Purpose: Connect to the sql server
*
*************************************************************************/
-int sql_init_socket(int reload) {
-
- int i;
+int
+sql_init_socket(SQL_INST *inst)
+{
- /* Clear up old connections if reload */
- if (reload)
- for (i = 0; i < sql->config->max_sql_socks; i++)
- if (!sql_close_socket(sql->socks[i]))
- radlog(L_CONS|L_ERR, "Could not release socket %d", i);
-
+ int i;
- /* Initalize our connection pool */
- for (i = 0; i < sql->config->max_sql_socks; i++) {
- if ((sql->socks[i] = sql_create_socket()) == NULL) {
- radlog(L_CONS|L_ERR, "SQL: Failed to connect socket %d", i);
+ /*
+ * Initalize our connection pool
+ */
+ for (i = 0; i < inst->config->num_sql_socks; i++) {
+ if ((inst->socks[i] = sql_create_socket(inst)) == NULL) {
+ radlog(L_CONS | L_ERR, "rlm_sql: Failed to connect socket %d", i);
+ return -1;
} else {
- sql->socks[i]->id = i;
- sql->socks[i]->in_use = 0;
- DEBUG2("SQL: Connected socket %d", i);
+ inst->socks[i]->id = i;
+ inst->socks[i]->in_use = 0;
+ DEBUG2("rlm_sql: Connected socket %d", i);
}
}
-
- return 1;
+
+ return 1;
}
* Purpose: Close and free a sql socket
*
*************************************************************************/
-int sql_close_socket(SQLSOCK *socket) {
+int
+sql_close_socket(SQLSOCK * socket)
+{
- DEBUG2("SQL: Closing socket %d", socket->id);
+ DEBUG2("rlm_sql: Closing socket %d", socket->id);
sql_close(socket);
free(socket);
return 1;
}
-
+
/*************************************************************************
*
* Purpose: Return a SQL socket from the connection pool
*
*************************************************************************/
-SQLSOCK *sql_get_socket(void) {
+SQLSOCK *
+sql_get_socket(SQL_INST *inst)
+{
- int i = 0;
-
- DEBUG2("SQL: Attempting to reserve socket");
+ int i = 0;
+
+ DEBUG2("rlm_sql: Attempting to reserve socket");
+#if HAVE_PTHREAD_H
+ pthread_mutex_lock(&inst->sqlsock_mutex);
+#endif
while (1) {
- if (i == sql->config->max_sql_socks)
+ if (i == inst->config->num_sql_socks)
i = 0;
- if (sql->socks[i]->in_use == 0) {
- sql->socks[i]->in_use = 1;
- gettimeofday(&(sql->socks[i]->tv), NULL);
- DEBUG2("SQL: Reserved socket %d", i);
- return sql->socks[i];
+ if (inst->socks[i]->in_use == 0) {
+ inst->socks[i]->in_use = 1;
+ gettimeofday(&(inst->socks[i]->tv), NULL);
+ DEBUG2("rlm_sql: Reserved socket %d", i);
+ break;
}
i++;
}
+#if HAVE_PTHREAD_H
+ pthread_mutex_unlock(&inst->sqlsock_mutex);
+#endif
+ return inst->socks[i];
}
/*************************************************************************
* Purpose: Frees a SQL socket back to the connection pool
*
*************************************************************************/
-int sql_release_socket(SQLSOCK *socket) {
+int
+sql_release_socket(SQL_INST *inst, SQLSOCK * socket)
+{
- struct timeval tv;
- double start, end;
- char buff[24];
+ struct timeval tv;
+ double start, end;
+ char buff[24];
gettimeofday(&tv, NULL);
sprintf(buff, "%ld.%2ld", tv.tv_sec, tv.tv_usec);
end = strtod(buff, NULL);
sprintf(buff, "%ld %2.0ld", socket->tv.tv_sec, socket->tv.tv_usec);
start = strtod(buff, NULL);
- DEBUG2("SQL: Socket %d used for %.2f seconds", socket->id, end - start);
+ DEBUG2("rlm_sql: Socket %d used for %.2f seconds", socket->id, end - start);
socket->tv.tv_sec = tv.tv_sec;
socket->tv.tv_usec = tv.tv_usec;
- sql->socks[socket->id]->in_use = 0;
- DEBUG2("SQL: Released socket %d", socket->id);
+ inst->socks[socket->id]->in_use = 0;
+ DEBUG2("rlm_sql: Released socket %d", socket->id);
return 1;
}
-
+
/*************************************************************************
*
* Function: sql_save_acct
*
*************************************************************************/
-int sql_save_acct(SQLSOCK *socket, SQLACCTREC *sqlrecord) {
+int
+sql_save_acct(SQL_INST *inst, SQLSOCK * socket, SQLACCTREC * sqlrecord)
+{
+
+ char querystr[2048];
+ FILE *sqlfile=0;
+ int num = 0;
+ int acctunique = 0;
- char querystr[2048];
- FILE *sqlfile;
- int num = 0;
#ifdef NT_DOMAIN_HACK
- char *ptr;
- char newname[AUTH_STRING_LEN];
+ char *ptr;
+ char newname[AUTH_STRING_LEN];
#endif
-
+ acctunique = strlen(sqlrecord->AcctUniqueId);
- if((sqlfile = fopen(SQLQUERYLOG, "a")) == (FILE *)NULL) {
- radlog(L_ERR, "Acct: Couldn't open file %s", SQLQUERYLOG);
- } else {
- #if defined(F_LOCK) && !defined(BSD)
- (void)lockf((int)sqlfile, (int)F_LOCK, (off_t)SQL_LOCK_LEN);
- #else
- (void)flock(sqlfile, SQL_LOCK_EX);
- #endif
- }
+ if(inst->config->sqltrace) {
+ if ((sqlfile = fopen(inst->config->tracefile, "a")) == (FILE *) NULL) {
+ radlog(L_ERR, "rlm_sql: Couldn't open file %s", inst->config->tracefile);
+ } else {
+#if defined(F_LOCK) && !defined(BSD)
+ (void) lockf((int) sqlfile, (int) F_LOCK, (off_t) SQL_LOCK_LEN);
+#else
+ (void) flock(sqlfile, SQL_LOCK_EX);
+#endif
+ }
+ }
#ifdef NT_DOMAIN_HACK
/*
newname[sizeof(newname) - 1] = 0;
strcpy(sqlrecord->UserName, newname);
}
-#endif /* NT_DOMAIN_HACK */
-
- if (sqlrecord->AcctStatusTypeId == PW_STATUS_ACCOUNTING_ON || sqlrecord->AcctStatusTypeId == PW_STATUS_ACCOUNTING_OFF) {
- radlog(L_INFO, "Portmaster %s rebooted at %s", sqlrecord->NASIPAddress, sqlrecord->AcctTimeStamp);
-
- /* The Terminal server informed us that it was rebooted
- * STOP all records from this NAS */
+#endif /*
+ * NT_DOMAIN_HACK
+ */
+
+ if (sqlrecord->AcctStatusTypeId == PW_STATUS_ACCOUNTING_ON ||
+ sqlrecord->AcctStatusTypeId == PW_STATUS_ACCOUNTING_OFF) {
+ radlog(L_INFO, "rlm_sql: Portmaster %s rebooted at %s", sqlrecord->NASIPAddress,
+ sqlrecord->AcctTimeStamp);
+
+ /*
+ * The Terminal server informed us that it was rebooted
+ * * STOP all records from this NAS
+ */
+
+ sprintf(querystr,
+ "UPDATE %s SET AcctStopTime='%s', AcctSessionTime=unix_timestamp('%s') - unix_timestamp(AcctStartTime), AcctTerminateCause='%s', AcctStopDelay = %ld WHERE AcctSessionTime=0 AND AcctStopTime=0 AND NASIPAddress= '%s' AND AcctStartTime <= '%s'",
+ inst->config->sql_acct_table, sqlrecord->AcctTimeStamp,
+ sqlrecord->AcctTimeStamp, sqlrecord->AcctTerminateCause,
+ sqlrecord->AcctDelayTime, sqlrecord->NASIPAddress,
+ sqlrecord->AcctTimeStamp);
+
+ if (sql_query(inst, socket, querystr) < 0)
+ radlog(L_ERR, "rlm_sql: Couldn't update SQL accounting after NAS reboot - %s",
+ sql_error(socket));
+ sql_finish_query(socket);
- sprintf(querystr, "UPDATE %s SET AcctStopTime='%s', AcctSessionTime=unix_timestamp('%s') - unix_timestamp(AcctStartTime), AcctTerminateCause='%s', AcctStopDelay = %ld WHERE AcctSessionTime=0 AND AcctStopTime=0 AND NASIPAddress= '%s' AND AcctStartTime <= '%s'", sql->config->sql_acct_table, sqlrecord->AcctTimeStamp, sqlrecord->AcctTimeStamp, sqlrecord->AcctTerminateCause, sqlrecord->AcctDelayTime, sqlrecord->NASIPAddress, sqlrecord->AcctTimeStamp);
+ if (sqlfile) {
+ fputs(querystr, sqlfile);
+ fputs(";\n", sqlfile);
+ fclose(sqlfile);
+ }
+ return 0;
+ }
- if (sql_query(socket, querystr) < 0)
- radlog(L_ERR, "Acct: Couldn't update SQL accounting after NAS reboot - %s", sql_error(socket));
- sql_finish_query(socket);
+ if (sqlrecord->AcctStatusTypeId == PW_STATUS_ALIVE) {
+ /*
+ * Use acct unique session identifier if present
+ */
+ if(acctunique) {
+ sprintf(querystr, "UPDATE %s SET FramedIPAddress = '%s' WHERE AcctUniqueId = '%s'",
+ inst->config->sql_acct_table, sqlrecord->FramedIPAddress,
+ sqlrecord->AcctUniqueId);
- if (sqlfile) {
- fputs(querystr, sqlfile);
- fputs(";\n", sqlfile);
- fclose(sqlfile);
- }
- return 0;
- }
+ } else {
+ sprintf(querystr, "UPDATE %s SET FramedIPAddress = '%s' WHERE AcctSessionId = '%s' AND UserName = '%s' AND NASIPAddress= '%s'",
+ inst->config->sql_acct_table, sqlrecord->FramedIPAddress,
+ sqlrecord->AcctSessionId, sqlrecord->UserName,
+ sqlrecord->NASIPAddress);
+ }
- if (sqlrecord->AcctStatusTypeId == PW_STATUS_ALIVE) {
- sprintf(querystr, "UPDATE %s SET FramedIPAddress = '%s' WHERE AcctSessionId = '%s' AND UserName = '%s' AND NASIPAddress= '%s'", sql->config->sql_acct_table, sqlrecord->FramedIPAddress, sqlrecord->AcctSessionId, sqlrecord->UserName, sqlrecord->NASIPAddress);
- if (sql_query(socket, querystr) < 0)
- radlog(L_ERR, "Acct: Couldn't update SQL accounting for ALIVE packet - %s", sql_error(socket));
+ if (sql_query(inst, socket, querystr) < 0)
+ radlog(L_ERR, "rlm_sql: Couldn't update SQL accounting for ALIVE packet - %s",
+ sql_error(socket));
sql_finish_query(socket);
if (sqlfile) {
return 0;
}
- /* Got start record */
- if(sqlrecord->AcctStatusTypeId == PW_STATUS_START) {
-
- /* Set start time on record with only a stop record */
- snprintf(querystr, 2048, "UPDATE %s SET AcctStartTime = '%s', AcctStartDelay = %ld WHERE AcctSessionId = '%s' AND UserName = '%s' AND NASIPAddress = '%s'",
- sql->config->sql_acct_table,
- sqlrecord->AcctTimeStamp,
- sqlrecord->AcctDelayTime,
- sqlrecord->AcctSessionId,
- sqlrecord->UserName,
- sqlrecord->NASIPAddress
- );
- if (sql_query(socket, querystr) < 0)
- radlog(L_ERR, "Acct: Couldn't update SQL accounting START record - %s", sql_error(socket));
- sql_finish_query(socket);
-
- num = sql_affected_rows(socket);
- if (num == 0) {
-
- /* Insert new record with blank stop time until stop record is got */
- snprintf(querystr, 2048, "INSERT INTO %s VALUES (0, '%s', '%s', '%s', '%s', %ld, '%s', '%s', 0, 0, '%s', '%s', 0, 0, '%s', '%s', '', '%s', '%s', '%s', %ld, 0)",
- sql->config->sql_acct_table,
- sqlrecord->AcctSessionId,
- sqlrecord->UserName,
- sqlrecord->Realm,
- sqlrecord->NASIPAddress,
- sqlrecord->NASPortId,
- sqlrecord->NASPortType,
- sqlrecord->AcctTimeStamp,
- sqlrecord->AcctAuthentic,
- sqlrecord->ConnectInfo,
- sqlrecord->CalledStationId,
- sqlrecord->CallingStationId,
- sqlrecord->ServiceType,
- sqlrecord->FramedProtocol,
- sqlrecord->FramedIPAddress,
- sqlrecord->AcctDelayTime
- );
-
- if (sql_query(socket, querystr) < 0)
- radlog(L_ERR, "Acct: Couldn't insert SQL accounting START record - %s", sql_error(socket));
+ /*
+ * Got start record
+ */
+ if (sqlrecord->AcctStatusTypeId == PW_STATUS_START) {
+
+ /*
+ * Insert new record with blank stop time until stop record is got
+ */
+ snprintf(querystr, 2048,
+ "INSERT INTO %s VALUES (0, '%s', '%s', '%s', '%s', '%s', %ld, '%s', '%s', 0, 0, '%s', '%s', '', 0, 0, '%s', '%s', '', '%s', '%s', '%s', %ld, 0)",
+ inst->config->sql_acct_table, sqlrecord->AcctSessionId,
+ sqlrecord->AcctUniqueId, sqlrecord->UserName,
+ sqlrecord->Realm, sqlrecord->NASIPAddress,
+ sqlrecord->NASPortId, sqlrecord->NASPortType,
+ sqlrecord->AcctTimeStamp, sqlrecord->AcctAuthentic,
+ sqlrecord->ConnectInfo, sqlrecord->CalledStationId,
+ sqlrecord->CallingStationId, sqlrecord->ServiceType,
+ sqlrecord->FramedProtocol, sqlrecord->FramedIPAddress,
+ sqlrecord->AcctDelayTime);
+
+ if (sql_query(inst, socket, querystr) < 0) {
+ radlog(L_ERR, "rlm_sql: Couldn't insert SQL accounting START record - %s",
+ sql_error(socket));
+
+ /*
+ * We failed the insert above. It's probably because
+ * the stop record came before the start. We try an
+ * update here to be sure
+ */
+ if(acctunique) {
+ snprintf(querystr, 2048, "UPDATE %s SET AcctStartTime = '%s', AcctStartDelay = %ld, ConnectInfo_start = '%s' WHERE AcctUniqueId = '%s'",
+ inst->config->sql_acct_table, sqlrecord->AcctTimeStamp,
+ sqlrecord->AcctDelayTime, sqlrecord->ConnectInfo,
+ sqlrecord->AcctUniqueId);
+ } else {
+ snprintf(querystr, 2048, "UPDATE %s SET AcctStartTime = '%s', AcctStartDelay = %ld, ConnectInfo_start = '%s' WHERE AcctSessionId = '%s' AND UserName = '%s' AND NASIPAddress = '%s'",
+ inst->config->sql_acct_table, sqlrecord->AcctTimeStamp,
+ sqlrecord->AcctDelayTime, sqlrecord->ConnectInfo,
+ sqlrecord->AcctSessionId, sqlrecord->UserName,
+ sqlrecord->NASIPAddress);
+ }
+ if (sql_query(inst, socket, querystr) < 0)
+ radlog(L_ERR, "rlm_sql: Couldn't update SQL accounting START record - %s",
+ sql_error(socket));
+
+ }
sql_finish_query(socket);
- }
-
- /* Got stop record */
- } else {
-
- sprintf(querystr, "SELECT RadAcctId FROM %s WHERE AcctSessionId='%s' AND NASIPAddress='%s' AND UserName='%s'", sql->config->sql_acct_table, sqlrecord->AcctSessionId, sqlrecord->NASIPAddress, sqlrecord->UserName);
- sql_select_query(socket, querystr);
- num = sql_num_rows(socket);
- sql_finish_select_query(socket);
-
- if (num > 0) {
-
- /* Set stop time on matching record with start time */
- snprintf(querystr, 2048, "UPDATE %s SET AcctStopTime = '%s', AcctSessionTime = '%lu', AcctInputOctets = '%lu', AcctOutputOctets = '%lu', AcctTerminateCause = '%s', AcctStopDelay = %ld WHERE AcctSessionId = '%s' AND UserName = '%s' AND NASIPAddress = '%s'",
- sql->config->sql_acct_table,
- sqlrecord->AcctTimeStamp,
- sqlrecord->AcctSessionTime,
- sqlrecord->AcctInputOctets,
- sqlrecord->AcctOutputOctets,
- sqlrecord->AcctTerminateCause,
- sqlrecord->AcctDelayTime,
- sqlrecord->AcctSessionId,
- sqlrecord->UserName,
- sqlrecord->NASIPAddress
- );
-
-
- if (sql_query(socket, querystr) < 0)
- radlog(L_ERR, "Acct: Couldn't update SQL accounting STOP record - %s", sql_error(socket));
+
+ /*
+ * Got stop record
+ */
+ } else {
+
+ /*
+ * Set stop time on matching record with start time
+ */
+ if(acctunique) {
+ snprintf(querystr, 2048,
+ "UPDATE %s SET AcctStopTime = '%s', AcctSessionTime = '%lu', AcctInputOctets = '%lu', AcctOutputOctets = '%lu', AcctTerminateCause = '%s', AcctStopDelay = %ld, ConnectInfo_stop = '%s' WHERE AcctUniqueId = '%s'",
+ inst->config->sql_acct_table, sqlrecord->AcctTimeStamp,
+ sqlrecord->AcctSessionTime, sqlrecord->AcctInputOctets,
+ sqlrecord->AcctOutputOctets, sqlrecord->AcctTerminateCause,
+ sqlrecord->AcctDelayTime, sqlrecord->ConnectInfo,
+ sqlrecord->AcctUniqueId);
+
+ } else {
+ snprintf(querystr, 2048,
+ "UPDATE %s SET AcctStopTime = '%s', AcctSessionTime = '%lu', AcctInputOctets = '%lu', AcctOutputOctets = '%lu', AcctTerminateCause = '%s', AcctStopDelay = %ld, ConnectInfo_stop = '%s' WHERE AcctSessionId = '%s' AND UserName = '%s' AND NASIPAddress = '%s'",
+ inst->config->sql_acct_table, sqlrecord->AcctTimeStamp,
+ sqlrecord->AcctSessionTime, sqlrecord->AcctInputOctets,
+ sqlrecord->AcctOutputOctets, sqlrecord->AcctTerminateCause,
+ sqlrecord->AcctDelayTime, sqlrecord->ConnectInfo,
+ sqlrecord->AcctSessionId, sqlrecord->UserName,
+ sqlrecord->NASIPAddress);
+ }
+
+
+ if (sql_query(inst, socket, querystr) < 0)
+ radlog(L_ERR, "rlm_sql: Couldn't update SQL accounting STOP record - %s",
+ sql_error(socket));
sql_finish_query(socket);
- } else if (num == 0) {
+
+ /*
+ * 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
+ */
+ num = sql_affected_rows(socket);
+ if(num < 1) {
#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 (sqlrecord->AcctSessionTime <= 0) {
- radlog(L_ERR, "Acct: Invalid STOP record. [%s] STOP record but zero session length? (nas %s)", sqlrecord->UserName, sqlrecord->NASIPAddress);
- return 0;
- }
+ /*
+ * 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 (sqlrecord->AcctSessionTime <= 0) {
+ radlog(L_ERR, "rlm_sql: Invalid STOP record. [%s] STOP record but zero session length? (nas %s)",
+ sqlrecord->UserName, sqlrecord->NASIPAddress);
+ return 0;
+ }
#endif
-
- /* Insert record with no start time until matching start record comes */
- snprintf(querystr, 2048, "INSERT INTO %s VALUES (0, '%s', '%s', '%s', '%s', %ld, '%s', 0, '%s', '%lu', '%s', '%s', '%lu', '%lu', '%s', '%s', '%s', '%s', '%s', '%s', 0, %ld)",
- sql->config->sql_acct_table,
- sqlrecord->AcctSessionId,
- sqlrecord->UserName,
- sqlrecord->Realm,
- sqlrecord->NASIPAddress,
- sqlrecord->NASPortId,
- sqlrecord->NASPortType,
- sqlrecord->AcctTimeStamp,
- sqlrecord->AcctSessionTime,
- sqlrecord->AcctAuthentic,
- sqlrecord->ConnectInfo,
- sqlrecord->AcctInputOctets,
- sqlrecord->AcctOutputOctets,
- sqlrecord->CalledStationId,
- sqlrecord->CallingStationId,
- sqlrecord->AcctTerminateCause,
- sqlrecord->ServiceType,
- sqlrecord->FramedProtocol,
- sqlrecord->FramedIPAddress,
- sqlrecord->AcctDelayTime
- );
-
- if (sql_query(socket, querystr) < 0)
- radlog(L_ERR, "Acct: Couldn't insert SQL accounting STOP record - %s", sql_error(socket));
- sql_finish_query(socket);
- }
- }
- if (sqlfile) {
- fputs(querystr, sqlfile);
- fputs(";\n", sqlfile);
- fflush(sqlfile);
- fclose(sqlfile);
- }
+ /*
+ * Insert record with no start time until matching start record comes
+ */
+ snprintf(querystr, 2048,
+ "INSERT INTO %s VALUES (0, '%s', '%s', '%s', '%s', '%s', %ld, '%s', 0, '%s', '%lu', '%s', '', '%s', '%lu', '%lu', '%s', '%s', '%s', '%s', '%s', '%s', 0, %ld)",
+ inst->config->sql_acct_table, sqlrecord->AcctSessionId,
+ sqlrecord->AcctUniqueId, sqlrecord->UserName,
+ sqlrecord->Realm, sqlrecord->NASIPAddress,
+ sqlrecord->NASPortId, sqlrecord->NASPortType,
+ sqlrecord->AcctTimeStamp, sqlrecord->AcctSessionTime,
+ sqlrecord->AcctAuthentic, sqlrecord->ConnectInfo,
+ sqlrecord->AcctInputOctets, sqlrecord->AcctOutputOctets,
+ sqlrecord->CalledStationId, sqlrecord->CallingStationId,
+ sqlrecord->AcctTerminateCause, sqlrecord->ServiceType,
+ sqlrecord->FramedProtocol, sqlrecord->FramedIPAddress,
+ sqlrecord->AcctDelayTime);
+
+ if (sql_query(inst, socket, querystr) < 0)
+ radlog(L_ERR, "rlm_sql: Couldn't insert SQL accounting STOP record - %s",
+ sql_error(socket));
+ sql_finish_query(socket);
+ }
- return 0;
+ }
+ if (sqlfile) {
+ fputs(querystr, sqlfile);
+ fputs(";\n", sqlfile);
+ fflush(sqlfile);
+ fclose(sqlfile);
+ }
+
+ return 0;
}
* Purpose: Read entries from the database and fill VALUE_PAIR structures
*
*************************************************************************/
-int sql_userparse(VALUE_PAIR **first_pair, SQL_ROW row, int mode) {
+int
+sql_userparse(VALUE_PAIR ** first_pair, SQL_ROW row, int mode)
+{
- DICT_ATTR *attr;
- VALUE_PAIR *pair, *check;
+ DICT_ATTR *attr;
+ VALUE_PAIR *pair, *check;
- if((attr = dict_attrbyname(row[2])) == (DICT_ATTR *)NULL) {
- radlog(L_ERR|L_CONS, "unknown attribute %s", row[2]);
- return(-1);
- }
+ if ((attr = dict_attrbyname(row[2])) == (DICT_ATTR *) NULL) {
+ radlog(L_ERR | L_CONS, "rlm_sql: unknown attribute %s", row[2]);
+ return (-1);
+ }
- /* If attribute is already there, skip it because we checked usercheck first
- and we want user settings to over ride group settings */
- if ((check = pairfind(*first_pair, attr->attr)) != NULL && mode == PW_VP_GROUPDATA)
- return 0;
+ /*
+ * If attribute is already there, skip it because we checked usercheck first
+ * and we want user settings to over ride group settings
+ */
+ if ((check = pairfind(*first_pair, attr->attr)) != NULL &&
+ mode == PW_VP_GROUPDATA) return 0;
- pair = pairmake(row[2], row[3], T_OP_CMP_EQ);
- pairadd(first_pair, pair);
-
- return 0;
+ pair = pairmake(row[2], row[3], T_OP_CMP_EQ);
+ pairadd(first_pair, pair);
+
+ return 0;
}
* Purpose: Get any group check or reply pairs
*
*************************************************************************/
-int sql_getvpdata(SQLSOCK *socket, char *table, VALUE_PAIR **vp, char *user, int mode) {
+int
+sql_getvpdata(SQL_INST *inst, SQLSOCK * socket, char *table, VALUE_PAIR ** vp, char *user,
+ int mode)
+{
- char querystr[256];
- char authstr[256];
- char username[AUTH_STRING_LEN*2+1];
- SQL_ROW row;
- int rows;
- int length;
+ char querystr[256];
+ char authstr[256];
+ char username[AUTH_STRING_LEN * 2 + 1];
+ SQL_ROW row;
+ int rows;
+ int length;
if (strlen(user) > AUTH_STRING_LEN)
length = AUTH_STRING_LEN;
else
length = strlen(user);
- /* FIXME CHECK user for weird charactors!! */
+ /*
+ * FIXME CHECK user for weird charactors!!
+ */
sql_escape_string(username, user, length);
if (mode == PW_VP_USERDATA) {
- if (sql->config->sensitiveusername)
+ if (inst->config->sensitiveusername)
sprintf(authstr, "STRCMP(Username, '%s') = 0", username);
else
sprintf(authstr, "UserName = '%s'", username);
- sprintf(querystr, "SELECT * FROM %s WHERE %s ORDER BY id", table, authstr);
+ sprintf(querystr, "SELECT * FROM %s WHERE %s ORDER BY id", table,
+ authstr);
} else if (mode == PW_VP_GROUPDATA) {
- if (sql->config->sensitiveusername)
- sprintf(authstr, "STRCMP(%s.Username, '%s') = 0", sql->config->sql_usergroup_table, username);
+ if (inst->config->sensitiveusername)
+ sprintf(authstr, "STRCMP(%s.Username, '%s') = 0",
+ inst->config->sql_usergroup_table, username);
else
- sprintf(authstr, "%s.UserName = '%s'",sql->config->sql_usergroup_table, username);
- sprintf(querystr, "SELECT %s.* FROM %s, %s WHERE %s AND %s.GroupName = %s.GroupName ORDER BY %s.id", table, table, sql->config->sql_usergroup_table, authstr, sql->config->sql_usergroup_table, table, table);
+ sprintf(authstr, "%s.UserName = '%s'", inst->config->sql_usergroup_table,
+ username);
+ sprintf(querystr,
+ "SELECT %s.* FROM %s, %s WHERE %s AND %s.GroupName = %s.GroupName ORDER BY %s.id",
+ table, table, inst->config->sql_usergroup_table, authstr,
+ inst->config->sql_usergroup_table, table, table);
} else if (mode == PW_VP_REALMDATA)
- sprintf(querystr, "SELECT %s.* FROM %s, %s WHERE %s.RealmName = '%s' AND %s.GroupName = %s.GroupName ORDER BY %s.id", table, table, sql->config->sql_realmgroup_table, sql->config->sql_realmgroup_table, username, sql->config->sql_realmgroup_table, table, table);
- sql_select_query(socket, querystr);
+ sprintf(querystr,
+ "SELECT %s.* FROM %s, %s WHERE %s.RealmName = '%s' AND %s.GroupName = %s.GroupName ORDER BY %s.id",
+ table, table, inst->config->sql_realmgroup_table,
+ inst->config->sql_realmgroup_table, username,
+ inst->config->sql_realmgroup_table, table, table);
+ sql_select_query(inst, socket, querystr);
rows = sql_num_rows(socket);
while ((row = sql_fetch_row(socket))) {
if (sql_userparse(vp, row, mode) != 0) {
- radlog(L_ERR|L_CONS, "Error getting data from database");
+ radlog(L_ERR | L_CONS, "rlm_sql: Error getting data from database");
sql_finish_select_query(socket);
return -1;
}
static int got_alrm;
-static void alrm_handler()
+static void
+alrm_handler()
{
got_alrm = 1;
}
* Purpose: Checks the terminal server for a spacific login entry
*
*************************************************************************/
-static int sql_check_ts(SQL_ROW row) {
+static int
+sql_check_ts(SQL_ROW row)
+{
int pid, st, e;
int n;
- NAS *nas;
+ NAS *nas;
char session_id[12];
- char *s;
- void (*handler)(int);
+ char *s;
+ void (*handler) (int);
/*
* Find NAS type.
*/
if ((nas = nas_find(ip_addr(row[4]))) == NULL) {
- radlog(L_ERR, "Accounting: unknown NAS [%s]", row[4]);
- return -1;
- }
-
- /*
- * Fork.
- */
- handler = signal(SIGCHLD, SIG_DFL);
- if ((pid = fork()) < 0) {
- radlog(L_ERR, "Accounting: fork: %s", strerror(errno));
- signal(SIGCHLD, handler);
- return -1;
- }
-
- if (pid > 0) {
- /*
- * Parent - Wait for checkrad to terminate.
- * We timeout in 10 seconds.
- */
- got_alrm = 0;
- signal(SIGALRM, alrm_handler);
- alarm(10);
- while((e = waitpid(pid, &st, 0)) != pid)
- if (e < 0 && (errno != EINTR || got_alrm))
- break;
- alarm(0);
- signal(SIGCHLD, handler);
- if (got_alrm) {
- kill(pid, SIGTERM);
- sleep(1);
- kill(pid, SIGKILL);
- radlog(L_ERR, "Check-TS: timeout waiting for checkrad");
- return 2;
- }
- if (e < 0) {
- radlog(L_ERR, "Check-TS: unknown error in waitpid()");
- return 2;
- }
- return WEXITSTATUS(st);
- }
-
- /*
- * Child - exec checklogin with the right parameters.
- */
- for (n = 32; n >= 3; n--)
- close(n);
-
- sprintf(session_id, "%.8s", row[1]);
-
- s = CHECKRAD2;
- execl(CHECKRAD2, "checkrad", nas->nastype, row[4], row[5],
- row[2], session_id, NULL);
- if (errno == ENOENT) {
- s = CHECKRAD1;
- execl(CHECKRAD1, "checklogin", nas->nastype, row[4], row[5],
- row[2], session_id, NULL);
- }
- radlog(L_ERR, "Check-TS: exec %s: %s", s, strerror(errno));
-
- /*
- * Exit - 2 means "some error occured".
- */
- exit(2);
+ radlog(L_ERR, "rlm_sql: unknown NAS [%s]", row[4]);
+ return -1;
+ }
+
+ /*
+ * Fork.
+ */
+ handler = signal(SIGCHLD, SIG_DFL);
+ if ((pid = fork()) < 0) {
+ radlog(L_ERR, "rlm_sql: fork: %s", strerror(errno));
+ signal(SIGCHLD, handler);
+ return -1;
+ }
+
+ if (pid > 0) {
+ /*
+ * Parent - Wait for checkrad to terminate.
+ * We timeout in 10 seconds.
+ */
+ got_alrm = 0;
+ signal(SIGALRM, alrm_handler);
+ alarm(10);
+ while ((e = waitpid(pid, &st, 0)) != pid)
+ if (e < 0 && (errno != EINTR || got_alrm))
+ break;
+ alarm(0);
+ signal(SIGCHLD, handler);
+ if (got_alrm) {
+ kill(pid, SIGTERM);
+ sleep(1);
+ kill(pid, SIGKILL);
+ radlog(L_ERR, "rlm_sql: Check-TS: timeout waiting for checkrad");
+ return 2;
+ }
+ if (e < 0) {
+ radlog(L_ERR, "rlm_sql: Check-TS: unknown error in waitpid()");
+ return 2;
+ }
+ return WEXITSTATUS(st);
+ }
+
+ /*
+ * Child - exec checklogin with the right parameters.
+ */
+ for (n = 32; n >= 3; n--)
+ close(n);
+
+ sprintf(session_id, "%.8s", row[1]);
+
+ s = CHECKRAD2;
+ execl(CHECKRAD2, "checkrad", nas->nastype, row[4], row[5],
+ row[2], session_id, NULL);
+ if (errno == ENOENT) {
+ s = CHECKRAD1;
+ execl(CHECKRAD1, "checklogin", nas->nastype, row[4], row[5],
+ row[2], session_id, NULL);
+ }
+ radlog(L_ERR, "rlm_sql: Check-TS: exec %s: %s", s, strerror(errno));
+
+ /*
+ * Exit - 2 means "some error occured".
+ */
+ exit(2);
}
* Purpose: Check radius accounting for duplicate logins
*
*************************************************************************/
-int sql_check_multi(SQLSOCK *socket, char *name, VALUE_PAIR *request, int maxsimul) {
+int
+sql_check_multi(SQL_INST *inst, SQLSOCK * socket, char *name, VALUE_PAIR * request,
+ int maxsimul)
+{
- char querystr[256];
- char authstr[256];
- VALUE_PAIR *fra;
- SQL_ROW row;
- int count = 0;
- uint32_t ipno = 0;
- int mpp = 1;
+ char querystr[256];
+ char authstr[256];
+ VALUE_PAIR *fra;
+ SQL_ROW row;
+ int count = 0;
+ uint32_t ipno = 0;
+ int mpp = 1;
- if (sql->config->sensitiveusername)
+ if (inst->config->sensitiveusername)
sprintf(authstr, "STRCMP(UserName, '%s') = 0", name);
else
sprintf(authstr, "UserName = '%s'", name);
- sprintf(querystr, "SELECT COUNT(*) FROM %s WHERE %s AND AcctStopTime = 0", sql->config->sql_acct_table, authstr);
- sql_select_query(socket, querystr);
+ sprintf(querystr, "SELECT COUNT(*) FROM %s WHERE %s AND AcctStopTime = 0",
+ inst->config->sql_acct_table, authstr);
+ sql_select_query(inst, socket, querystr);
row = sql_fetch_row(socket);
count = atoi(row[0]);
sql_finish_select_query(socket);
return 0;
/*
- * Setup some stuff, like for MPP detection.
- */
+ * * Setup some stuff, like for MPP detection.
+ */
if ((fra = pairfind(request, PW_FRAMED_IP_ADDRESS)) != NULL)
ipno = htonl(fra->lvalue);
count = 0;
- sprintf(querystr, "SELECT * FROM %s WHERE %s AND AcctStopTime = 0", sql->config->sql_acct_table, authstr);
- sql_select_query(socket, querystr);
+ sprintf(querystr, "SELECT * FROM %s WHERE %s AND AcctStopTime = 0",
+ inst->config->sql_acct_table, authstr);
+ sql_select_query(inst, socket, querystr);
while ((row = sql_fetch_row(socket))) {
- int check = sql_check_ts(row);
+ int check = sql_check_ts(row);
+
if (check == 1) {
count++;
if (ipno && atoi(row[19]) == ipno)
- mpp = 2;
+ mpp = 2;
} else if (check == 2)
- radlog(L_ERR,"Problem with checkrad [%s] (from nas %s)", name, row[4]);
+ radlog(L_ERR, "rlm_sql: Problem with checkrad [%s] (from nas %s)", name, row[4]);
else {
/*
- * False record - zap it
+ * False record - zap it
*/
- if (sql->config->deletestalesessions) {
+ if (inst->config->deletestalesessions) {
SQLSOCK *socket;
- radlog(L_ERR,"Deleteing stale session [%s] (from nas %s/%s)", row[2], row[4], row[5]);
- socket = sql_get_socket();
- sprintf(querystr, "DELETE FROM %s WHERE RadAcctId = '%s'", sql->config->sql_acct_table, row[0]);
- sql_query(socket, querystr);
+ radlog(L_ERR, "rlm_sql: Deleteing stale session [%s] (from nas %s/%s)", row[2],
+ row[4], row[5]);
+ socket = sql_get_socket(inst);
+ sprintf(querystr, "DELETE FROM %s WHERE RadAcctId = '%s'",
+ inst->config->sql_acct_table, row[0]);
+ sql_query(inst, socket, querystr);
sql_finish_query(socket);
sql_close_socket(socket);
}
}
sql_finish_select_query(socket);
- return (count < maxsimul) ? 0 : mpp;
+ return (count < maxsimul) ? 0 : mpp;
}
/*************************************************************************
*
- * Function: sql_query
+ * Function: sql_create_socket
*
- * Purpose: Issue a query to the database
+ * Purpose: Establish connection to the db
*
*************************************************************************/
-SQLSOCK *sql_create_socket(void) {
+SQLSOCK *sql_create_socket(SQL_INST *inst) {
SQLSOCK *socket;
if ((socket = malloc(sizeof(SQLSOCK))) == NULL) {
}
mysql_init(&(socket->conn));
- if (!(socket->sock = mysql_real_connect(&(socket->conn), sql->config->sql_server, sql->config->sql_login, sql->config->sql_password, sql->config->sql_db, 0, NULL, 0))) {
- radlog(L_ERR, "Init: Couldn't connect socket to MySQL server %s@%s:%s", sql->config->sql_login, sql->config->sql_server, sql->config->sql_db);
+ if (!(socket->sock = mysql_real_connect(&(socket->conn), inst->config->sql_server, inst->config->sql_login, inst->config->sql_password, inst->config->sql_db, 0, NULL, CLIENT_FOUND_ROWS))) {
+ radlog(L_ERR, "rlm_sql: Couldn't connect socket to MySQL server %s@%s:%s", inst->config->sql_login, inst->config->sql_server, inst->config->sql_db);
+ radlog(L_ERR, "rlm_sql: Mysql error '%s'", mysql_error(&socket->conn));
socket->sock = NULL;
return NULL;
}
* Purpose: Issue a query to the database
*
*************************************************************************/
-int sql_query(SQLSOCK *socket, char *querystr) {
+int sql_query(SQL_INST *inst, SQLSOCK *socket, char *querystr) {
- if (sql->config->sqltrace)
+ if (inst->config->sqltrace)
DEBUG(querystr);
if (socket->sock == NULL) {
radlog(L_ERR, "Socket not connected");
* Purpose: Issue a select query to the database
*
*************************************************************************/
-int sql_select_query(SQLSOCK *socket, char *querystr) {
+int sql_select_query(SQL_INST *inst, SQLSOCK *socket, char *querystr) {
- if (sql->config->sqltrace)
+ if (inst->config->sqltrace)
DEBUG(querystr);
if (socket->sock == NULL) {
radlog(L_ERR, "Socket not connected");
#include <mysql/mysql.h>
#include "conf.h"
-typedef MYSQL_ROW SQL_ROW;
+typedef MYSQL_ROW SQL_ROW;
-typedef struct sql_config {
- char *sql_server;
- char *sql_login;
- char *sql_password;
- char *sql_db;
- char *sql_acct_table;
- char *sql_authcheck_table;
- char *sql_authreply_table;
- char *sql_groupcheck_table;
- char *sql_groupreply_table;
- char *sql_usergroup_table;
- char *sql_realm_table;
- char *sql_realmgroup_table;
- char *sql_nas_table;
- char *sql_dict_table;
- int sensitiveusername;
- int sqltrace;
- int deletestalesessions;
- int max_sql_socks;
-} SQL_CONFIG;
-
typedef struct sql_socket {
- MYSQL *sock;
- MYSQL conn;
- MYSQL_RES *result;
- int id;
- int in_use;
- struct timeval tv;
+ MYSQL *sock;
+ MYSQL conn;
+ MYSQL_RES *result;
+ int id;
+ int in_use;
+ struct timeval tv;
} SQLSOCK;
-
-typedef struct sql {
- SQL_CONFIG *config;
- SQLSOCK *socks[MAX_SQL_SOCKS];
-} SQL;
+typedef struct sql_config {
+ char *sql_server;
+ char *sql_login;
+ char *sql_password;
+ char *sql_db;
+ char *sql_acct_table;
+ char *sql_authcheck_table;
+ char *sql_authreply_table;
+ char *sql_groupcheck_table;
+ char *sql_groupreply_table;
+ char *sql_usergroup_table;
+ char *sql_realm_table;
+ char *sql_realmgroup_table;
+ char *sql_nas_table;
+ char *sql_dict_table;
+ int sensitiveusername;
+ int sqltrace;
+ char *tracefile;
+ int deletestalesessions;
+ int num_sql_socks;
+} SQL_CONFIG;
+
+typedef struct sql {
+ SQL_CONFIG *config;
+ SQLSOCK *socks[MAX_SQL_SOCKS];
+#if HAVE_PTHREAD_H
+ pthread_mutex_t sqlsock_mutex;
+#endif
+} SQL_INST;
-SQLSOCK *sql_create_socket(void);
-int sql_checksocket(const char *facility);
-int sql_query(SQLSOCK *socket, char *querystr);
-int sql_select_query(SQLSOCK *socket, char *querystr);
-int sql_store_result(SQLSOCK *socket);
-int sql_num_fields(SQLSOCK *socket);
-int sql_num_rows(SQLSOCK *socket);
-SQL_ROW sql_fetch_row(SQLSOCK *socket);
-void sql_free_result(SQLSOCK *socket);
-char *sql_error(SQLSOCK *socket);
-void sql_close(SQLSOCK *socket);
-void sql_finish_query(SQLSOCK *socket);
-void sql_finish_select_query(SQLSOCK *socket);
-int sql_affected_rows(SQLSOCK *socket);
-int sql_escape_string(char *to, char *from, int length);
+SQLSOCK *sql_create_socket(SQL_INST *inst);
+int sql_checksocket(const char *facility);
+int sql_query(SQL_INST *inst, SQLSOCK * socket, char *querystr);
+int sql_select_query(SQL_INST *inst, SQLSOCK * socket, char *querystr);
+int sql_store_result(SQLSOCK * socket);
+int sql_num_fields(SQLSOCK * socket);
+int sql_num_rows(SQLSOCK * socket);
+SQL_ROW sql_fetch_row(SQLSOCK * socket);
+void sql_free_result(SQLSOCK * socket);
+char *sql_error(SQLSOCK * socket);
+void sql_close(SQLSOCK * socket);
+void sql_finish_query(SQLSOCK * socket);
+void sql_finish_select_query(SQLSOCK * socket);
+int sql_affected_rows(SQLSOCK * socket);
+int sql_escape_string(char *to, char *from, int length);
/*************************************************************************
*
- * Function: sql_query
+ * Function: sql_create_socket
*
- * Purpose: Issue a query to the database
+ * Purpose: Establish connection to the db
*
*************************************************************************/
-SQLSOCK *sql_create_socket(void) {
+SQLSOCK *sql_create_socket(SQL_INST *inst) {
SQLSOCK *socket;
if ((socket = malloc(sizeof(SQLSOCK))) == NULL) {
}
mysql_init(&(socket->conn));
- if (!(socket->sock = mysql_real_connect(&(socket->conn), sql->config->sql_server, sql->config->sql_login, sql->config->sql_password, sql->config->sql_db, 0, NULL, 0))) {
- radlog(L_ERR, "Init: Couldn't connect socket to MySQL server %s@%s:%s", sql->config->sql_login, sql->config->sql_server, sql->config->sql_db);
+ if (!(socket->sock = mysql_real_connect(&(socket->conn), inst->config->sql_server, inst->config->sql_login, inst->config->sql_password, inst->config->sql_db, 0, NULL, CLIENT_FOUND_ROWS))) {
+ radlog(L_ERR, "rlm_sql: Couldn't connect socket to MySQL server %s@%s:%s", inst->config->sql_login, inst->config->sql_server, inst->config->sql_db);
+ radlog(L_ERR, "rlm_sql: Mysql error '%s'", mysql_error(&socket->conn));
socket->sock = NULL;
return NULL;
}
* Purpose: Issue a query to the database
*
*************************************************************************/
-int sql_query(SQLSOCK *socket, char *querystr) {
+int sql_query(SQL_INST *inst, SQLSOCK *socket, char *querystr) {
- if (sql->config->sqltrace)
+ if (inst->config->sqltrace)
DEBUG(querystr);
if (socket->sock == NULL) {
radlog(L_ERR, "Socket not connected");
* Purpose: Issue a select query to the database
*
*************************************************************************/
-int sql_select_query(SQLSOCK *socket, char *querystr) {
+int sql_select_query(SQL_INST *inst, SQLSOCK *socket, char *querystr) {
- if (sql->config->sqltrace)
+ if (inst->config->sqltrace)
DEBUG(querystr);
if (socket->sock == NULL) {
radlog(L_ERR, "Socket not connected");
#include <mysql/mysql.h>
#include "conf.h"
-typedef MYSQL_ROW SQL_ROW;
+typedef MYSQL_ROW SQL_ROW;
-typedef struct sql_config {
- char *sql_server;
- char *sql_login;
- char *sql_password;
- char *sql_db;
- char *sql_acct_table;
- char *sql_authcheck_table;
- char *sql_authreply_table;
- char *sql_groupcheck_table;
- char *sql_groupreply_table;
- char *sql_usergroup_table;
- char *sql_realm_table;
- char *sql_realmgroup_table;
- char *sql_nas_table;
- char *sql_dict_table;
- int sensitiveusername;
- int sqltrace;
- int deletestalesessions;
- int max_sql_socks;
-} SQL_CONFIG;
-
typedef struct sql_socket {
- MYSQL *sock;
- MYSQL conn;
- MYSQL_RES *result;
- int id;
- int in_use;
- struct timeval tv;
+ MYSQL *sock;
+ MYSQL conn;
+ MYSQL_RES *result;
+ int id;
+ int in_use;
+ struct timeval tv;
} SQLSOCK;
-
-typedef struct sql {
- SQL_CONFIG *config;
- SQLSOCK *socks[MAX_SQL_SOCKS];
-} SQL;
+typedef struct sql_config {
+ char *sql_server;
+ char *sql_login;
+ char *sql_password;
+ char *sql_db;
+ char *sql_acct_table;
+ char *sql_authcheck_table;
+ char *sql_authreply_table;
+ char *sql_groupcheck_table;
+ char *sql_groupreply_table;
+ char *sql_usergroup_table;
+ char *sql_realm_table;
+ char *sql_realmgroup_table;
+ char *sql_nas_table;
+ char *sql_dict_table;
+ int sensitiveusername;
+ int sqltrace;
+ char *tracefile;
+ int deletestalesessions;
+ int num_sql_socks;
+} SQL_CONFIG;
+
+typedef struct sql {
+ SQL_CONFIG *config;
+ SQLSOCK *socks[MAX_SQL_SOCKS];
+#if HAVE_PTHREAD_H
+ pthread_mutex_t sqlsock_mutex;
+#endif
+} SQL_INST;
-SQLSOCK *sql_create_socket(void);
-int sql_checksocket(const char *facility);
-int sql_query(SQLSOCK *socket, char *querystr);
-int sql_select_query(SQLSOCK *socket, char *querystr);
-int sql_store_result(SQLSOCK *socket);
-int sql_num_fields(SQLSOCK *socket);
-int sql_num_rows(SQLSOCK *socket);
-SQL_ROW sql_fetch_row(SQLSOCK *socket);
-void sql_free_result(SQLSOCK *socket);
-char *sql_error(SQLSOCK *socket);
-void sql_close(SQLSOCK *socket);
-void sql_finish_query(SQLSOCK *socket);
-void sql_finish_select_query(SQLSOCK *socket);
-int sql_affected_rows(SQLSOCK *socket);
-int sql_escape_string(char *to, char *from, int length);
+SQLSOCK *sql_create_socket(SQL_INST *inst);
+int sql_checksocket(const char *facility);
+int sql_query(SQL_INST *inst, SQLSOCK * socket, char *querystr);
+int sql_select_query(SQL_INST *inst, SQLSOCK * socket, char *querystr);
+int sql_store_result(SQLSOCK * socket);
+int sql_num_fields(SQLSOCK * socket);
+int sql_num_rows(SQLSOCK * socket);
+SQL_ROW sql_fetch_row(SQLSOCK * socket);
+void sql_free_result(SQLSOCK * socket);
+char *sql_error(SQLSOCK * socket);
+void sql_close(SQLSOCK * socket);
+void sql_finish_query(SQLSOCK * socket);
+void sql_finish_select_query(SQLSOCK * socket);
+int sql_affected_rows(SQLSOCK * socket);
+int sql_escape_string(char *to, char *from, int length);