Jeff Carneal <jeff@apex.net>
authorjcarneal <jcarneal>
Sat, 4 Nov 2000 16:39:34 +0000 (16:39 +0000)
committerjcarneal <jcarneal>
Sat, 4 Nov 2000 16:39:34 +0000 (16:39 +0000)
Updated sql code to be instance aware.  Also did
the following:

1.  Added support for connectinfo start and stop
2.  Added support for acct unique (w/ updates)
3.  configurable num of connections
4.  configurable sql tracefile

TODO:  config-based accounting tables

raddb/radiusd.conf.in
src/include/radius.h
src/modules/rlm_sql/conf.h
src/modules/rlm_sql/db_mysql.sql
src/modules/rlm_sql/rlm_sql.c
src/modules/rlm_sql/rlm_sql.h
src/modules/rlm_sql/sql.c
src/modules/rlm_sql/sql_module.c
src/modules/rlm_sql/sql_module.h
src/modules/rlm_sql/sql_mysql.c
src/modules/rlm_sql/sql_mysql.h

index 713a484..d018369 100644 (file)
@@ -406,6 +406,7 @@ modules {
     usersfile = ${confdir}/users_fast
     hashsize = 1000
     compat = no
+               normal_defaults = yes
   }
        
        detail {
@@ -454,6 +455,10 @@ modules {
        
                # 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
        }
 
 #
index e2b3970..69b73dc 100644 (file)
@@ -67,6 +67,7 @@
 #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
index 4ba7a6d..2f366de 100644 (file)
 
 /* 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
index a16d62d..f5032bf 100644 (file)
@@ -48,6 +48,7 @@ CREATE TABLE nas (
 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,
@@ -57,7 +58,8 @@ CREATE TABLE radacct (
   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,
@@ -72,6 +74,7 @@ CREATE TABLE radacct (
   KEY UserName (UserName),
   KEY FramedIPAddress (FramedIPAddress),
   KEY AcctSessionId (AcctSessionId),
+  KEY AcctUniqueId (AcctUniqueId),
   KEY AcctStartTime (AcctStartTime),
   KEY AcctStopTime (AcctStopTime),
   KEY NASIPAddress (NASIPAddress)
index f3a9c3a..f178342 100644 (file)
@@ -6,7 +6,9 @@
 *                                                                          *
 *                                     Mike Machado <mike@innercite.com>    *
 ***************************************************************************/
-static const char rcsid[] = "$Id$";
+static const char rcsid[] =
+
+       "$Id$";
 
 #include "autoconf.h"
 
@@ -29,351 +31,484 @@ static const char rcsid[] = "$Id$";
 #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;
 }
@@ -381,15 +516,15 @@ static int rlm_sql_accounting(REQUEST *request) {
 
 /* 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 */
 };
index 7251c97..fb2ee7b 100644 (file)
@@ -1,3 +1,4 @@
+
 /***************************************************************************
 *  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);
 
index 9a8f5b3..6bcfd81 100644 (file)
 
 /*************************************************************************
  *
- *     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;
 }
 
 
@@ -132,14 +66,16 @@ int sql_init_socket(int reload) {
  *     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;
 }
+
 
 /*************************************************************************
  *
@@ -148,22 +84,31 @@ int sql_close_socket(SQLSOCK *socket) {
  *     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];
 }
 
 /*************************************************************************
@@ -173,27 +118,29 @@ SQLSOCK *sql_get_socket(void) {
  *     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
@@ -202,27 +149,33 @@ int sql_release_socket(SQLSOCK *socket) {
  *
  *************************************************************************/
 
-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
        /*
@@ -236,32 +189,59 @@ int sql_save_acct(SQLSOCK *socket, SQLACCTREC *sqlrecord) {
                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) {
@@ -272,128 +252,144 @@ int sql_save_acct(SQLSOCK *socket, SQLACCTREC *sqlrecord) {
                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;
 
 }
 
@@ -405,26 +401,30 @@ int sql_save_acct(SQLSOCK *socket, SQLACCTREC *sqlrecord) {
  *     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;
 }
 
 
@@ -435,43 +435,58 @@ int sql_userparse(VALUE_PAIR **first_pair, SQL_ROW row, int mode) {
  *     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;
                }
@@ -484,7 +499,8 @@ int sql_getvpdata(SQLSOCK *socket, char *table, VALUE_PAIR **vp, char *user, int
 
 
 static int got_alrm;
-static void alrm_handler()
+static void
+alrm_handler()
 {
        got_alrm = 1;
 }
@@ -496,82 +512,84 @@ static void alrm_handler()
  *     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);
 
 }
 
@@ -583,22 +601,26 @@ static int sql_check_ts(SQL_ROW row) {
  *     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);
@@ -607,36 +629,40 @@ int sql_check_multi(SQLSOCK *socket, char *name, VALUE_PAIR *request, int maxsim
                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);
                        }
@@ -644,6 +670,6 @@ int sql_check_multi(SQLSOCK *socket, char *name, VALUE_PAIR *request, int maxsim
        }
        sql_finish_select_query(socket);
 
-       return (count < maxsimul) ? 0 : mpp; 
+       return (count < maxsimul) ? 0 : mpp;
 
 }
index 819fd28..00ab3e4 100644 (file)
 
 /*************************************************************************
  *
- *     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) {
@@ -30,8 +30,9 @@ SQLSOCK *sql_create_socket(void) {
        }
 
        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;
        }
@@ -45,9 +46,9 @@ SQLSOCK *sql_create_socket(void) {
  *     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");
@@ -64,9 +65,9 @@ int sql_query(SQLSOCK *socket, char *querystr) {
  *     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");
index 59dd6ad..385c5ac 100644 (file)
@@ -8,56 +8,59 @@
 #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);
index 819fd28..00ab3e4 100644 (file)
 
 /*************************************************************************
  *
- *     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) {
@@ -30,8 +30,9 @@ SQLSOCK *sql_create_socket(void) {
        }
 
        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;
        }
@@ -45,9 +46,9 @@ SQLSOCK *sql_create_socket(void) {
  *     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");
@@ -64,9 +65,9 @@ int sql_query(SQLSOCK *socket, char *querystr) {
  *     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");
index 59dd6ad..385c5ac 100644 (file)
@@ -8,56 +8,59 @@
 #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);