Updated API for 2.2
[freeradius.git] / src / modules / rlm_counter / rlm_counter.c
index d38a339..d5ed5d2 100644 (file)
  *
  *   You should have received a copy of the GNU General Public License
  *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  *
- * Copyright 2001  The FreeRADIUS server project
+ * Copyright 2001,2006  The FreeRADIUS server project
  * Copyright 2001  Alan DeKok <aland@ox.org>
- * Copyright 2001,2002  Kostas Kalevras <kkalev@noc.ntua.gr>
+ * Copyright 2001-3  Kostas Kalevras <kkalev@noc.ntua.gr>
  */
 
-#include "config.h"
-#include "autoconf.h"
-#include "libradius.h"
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
+
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
 #include <ctype.h>
 
-#include "radiusd.h"
-#include "modules.h"
-#include "conffile.h"
+#include "config.h"
 
 #include <gdbm.h>
-#include <time.h>
 
 #ifdef NEEDS_GDBM_SYNC
 #      define GDBM_SYNCOPT GDBM_SYNC
@@ -56,8 +52,6 @@
 
 #define UNIQUEID_MAX_LEN 32
 
-static const char rcsid[] = "$Id$";
-
 /*
  *     Define a structure for our module configuration.
  *
@@ -72,12 +66,14 @@ typedef struct rlm_counter_t {
        char *count_attribute;  /* Acct-Session-Time */
        char *counter_name;     /* Daily-Session-Time */
        char *check_name;       /* Daily-Max-Session */
+       char *reply_name;       /* Session-Timeout */
        char *service_type;     /* Service-Type to search for */
        int cache_size;
        int service_val;
        int key_attr;
        int count_attr;
        int check_attr;
+       int reply_attr;
        time_t reset_time;      /* The time of the next reset. */
        time_t last_reset;      /* The time of the last reset. */
        int dict_attr;          /* attribute number for the counter. */
@@ -112,24 +108,29 @@ typedef struct rad_counter {
  *     to the strdup'd string into 'config.string'.  This gets around
  *     buffer over-flows.
  */
-static CONF_PARSER module_config[] = {
+static const CONF_PARSER module_config[] = {
   { "filename", PW_TYPE_STRING_PTR, offsetof(rlm_counter_t,filename), NULL, NULL },
   { "key", PW_TYPE_STRING_PTR, offsetof(rlm_counter_t,key_name), NULL, NULL },
   { "reset", PW_TYPE_STRING_PTR, offsetof(rlm_counter_t,reset), NULL,  NULL },
   { "count-attribute", PW_TYPE_STRING_PTR, offsetof(rlm_counter_t,count_attribute), NULL, NULL },
   { "counter-name", PW_TYPE_STRING_PTR, offsetof(rlm_counter_t,counter_name), NULL,  NULL },
   { "check-name", PW_TYPE_STRING_PTR, offsetof(rlm_counter_t,check_name), NULL, NULL },
+  { "reply-name", PW_TYPE_STRING_PTR, offsetof(rlm_counter_t,reply_name), NULL, NULL },
   { "allowed-servicetype", PW_TYPE_STRING_PTR, offsetof(rlm_counter_t,service_type),NULL, NULL },
   { "cache-size", PW_TYPE_INTEGER, offsetof(rlm_counter_t,cache_size), NULL, "1000" },
   { NULL, -1, 0, NULL, NULL }
 };
 
+static int counter_detach(void *instance);
+
 
 /*
  *     See if the counter matches.
  */
-static int counter_cmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
-               VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
+static int counter_cmp(void *instance,
+                      REQUEST *req UNUSED,
+                      VALUE_PAIR *request, VALUE_PAIR *check,
+                      VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
 {
        rlm_counter_t *data = (rlm_counter_t *) instance;
        datum key_datum;
@@ -139,16 +140,17 @@ static int counter_cmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_
 
        check_pairs = check_pairs; /* shut the compiler up */
        reply_pairs = reply_pairs;
+       req = req;
 
        /*
         *      Find the key attribute.
         */
-       key_vp = pairfind(request, data->key_attr);
+       key_vp = pairfind(request, data->key_attr, 0);
        if (key_vp == NULL) {
                return RLM_MODULE_NOOP;
        }
 
-       key_datum.dptr = key_vp->strvalue;
+       key_datum.dptr = key_vp->vp_strvalue;
        key_datum.dsize = key_vp->length;
 
        count_datum = gdbm_fetch(data->gdbm, key_datum);
@@ -159,19 +161,20 @@ static int counter_cmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_
        memcpy(&counter, count_datum.dptr, sizeof(rad_counter));
        free(count_datum.dptr);
 
-       return counter.user_counter - check->lvalue;
+       return counter.user_counter - check->vp_integer;
 }
 
+
 static int add_defaults(rlm_counter_t *data)
 {
        datum key_datum;
        datum time_datum;
        const char *default1 = "DEFAULT1";
        const char *default2 = "DEFAULT2";
-       
+
        DEBUG2("rlm_counter: add_defaults: Start");
 
-       key_datum.dptr = (const char *) default1;
+       key_datum.dptr = (char *) default1;
        key_datum.dsize = strlen(default1);
        time_datum.dptr = (char *) &data->reset_time;
        time_datum.dsize = sizeof(time_t);
@@ -183,7 +186,7 @@ static int add_defaults(rlm_counter_t *data)
        }
        DEBUG2("rlm_counter: DEFAULT1 set to %d",(int)data->reset_time);
 
-       key_datum.dptr = (const char *) default2;
+       key_datum.dptr = (char *) default2;
        key_datum.dsize = strlen(default2);
        time_datum.dptr = (char *) &data->last_reset;
        time_datum.dsize = sizeof(time_t);
@@ -226,7 +229,7 @@ static int reset_db(rlm_counter_t *data)
         */
        ret = add_defaults(data);
        if (ret != RLM_MODULE_OK)
-               return ret;     
+               return ret;
 
        DEBUG2("rlm_counter: reset_db ended");
 
@@ -235,19 +238,21 @@ static int reset_db(rlm_counter_t *data)
 
 static int find_next_reset(rlm_counter_t *data, time_t timeval)
 {
-       int ret=0;
-       unsigned int num=1;
-       char last = 0;
+       int ret = 0;
+       size_t len;
+       unsigned int num = 1;
+       char last = '\0';
        struct tm *tm, s_tm;
+       char sCurrentTime[40], sNextTime[40];
 
        tm = localtime_r(&timeval, &s_tm);
+       len = strftime(sCurrentTime, sizeof(sCurrentTime), "%Y-%m-%d %H:%M:%S", tm);
+       if (len == 0) *sCurrentTime = '\0';
        tm->tm_sec = tm->tm_min = 0;
 
        if (data->reset == NULL)
                return -1;
        if (isdigit((int) data->reset[0])){
-               unsigned int len=0;
-
                len = strlen(data->reset);
                if (len == 0)
                        return -1;
@@ -289,8 +294,11 @@ static int find_next_reset(rlm_counter_t *data, time_t timeval)
                        data->reset);
                return -1;
        }
-       DEBUG2("rlm_counter: Current Time: %d, Next reset %d", 
-               (int)timeval,(int)data->reset_time);
+
+       len = strftime(sNextTime, sizeof(sNextTime), "%Y-%m-%d %H:%M:%S", tm);
+       if (len == 0) *sNextTime = '\0';
+       DEBUG2("rlm_counter: Current Time: %li [%s], Next reset %li [%s]",
+               timeval, sCurrentTime, data->reset_time, sNextTime);
 
        return ret;
 }
@@ -319,12 +327,13 @@ static int counter_instantiate(CONF_SECTION *conf, void **instance)
        datum time_datum;
        const char *default1 = "DEFAULT1";
        const char *default2 = "DEFAULT2";
-       
+
        /*
         *      Set up a storage area for instance data
         */
        data = rad_malloc(sizeof(*data));
        if (!data) {
+               radlog(L_ERR, "rlm_counter: rad_malloc() failed.");
                return -1;
        }
        memset(data, 0, sizeof(*data));
@@ -340,49 +349,76 @@ static int counter_instantiate(CONF_SECTION *conf, void **instance)
        cache_size = data->cache_size;
 
        /*
-        *      Discover the attribute number of the key. 
+        *      Discover the attribute number of the key.
         */
        if (data->key_name == NULL) {
                radlog(L_ERR, "rlm_counter: 'key' must be set.");
-               exit(0);
+               counter_detach(data);
+               return -1;
        }
        dattr = dict_attrbyname(data->key_name);
        if (dattr == NULL) {
                radlog(L_ERR, "rlm_counter: No such attribute %s",
                                data->key_name);
+               counter_detach(data);
                return -1;
        }
        data->key_attr = dattr->attr;
-       
+
        /*
-        *      Discover the attribute number of the counter. 
+        *      Discover the attribute number of the counter.
         */
        if (data->count_attribute == NULL) {
                radlog(L_ERR, "rlm_counter: 'count-attribute' must be set.");
-               exit(0);
+               counter_detach(data);
+               return -1;
        }
        dattr = dict_attrbyname(data->count_attribute);
        if (dattr == NULL) {
                radlog(L_ERR, "rlm_counter: No such attribute %s",
                                data->count_attribute);
+               counter_detach(data);
                return -1;
        }
        data->count_attr = dattr->attr;
 
        /*
+        * Discover the attribute number of the reply attribute.
+        */
+       if (data->reply_name != NULL) {
+               dattr = dict_attrbyname(data->reply_name);
+               if (dattr == NULL) {
+                       radlog(L_ERR, "rlm_counter: No such attribute %s",
+                                       data->reply_name);
+                       counter_detach(data);
+                       return -1;
+               }
+               if (dattr->type != PW_TYPE_INTEGER) {
+                       radlog(L_ERR, "rlm_counter: Reply attribute %s is not of type integer",
+                               data->reply_name);
+                       counter_detach(data);
+                       return -1;
+               }
+               data->reply_attr = dattr->attr;
+       }
+
+
+       /*
         *  Create a new attribute for the counter.
         */
        if (data->counter_name == NULL) {
                radlog(L_ERR, "rlm_counter: 'counter-name' must be set.");
-               exit(0);
+               counter_detach(data);
+               return -1;
        }
 
        memset(&flags, 0, sizeof(flags));
-       dict_addattr(data->counter_name, 0, PW_TYPE_INTEGER, -1, flags);
+       dict_addattr(data->counter_name, -1, 0, PW_TYPE_INTEGER, flags);
        dattr = dict_attrbyname(data->counter_name);
        if (dattr == NULL) {
                radlog(L_ERR, "rlm_counter: Failed to create counter attribute %s",
                                data->counter_name);
+               counter_detach(data);
                return -1;
        }
        data->dict_attr = dattr->attr;
@@ -394,13 +430,15 @@ static int counter_instantiate(CONF_SECTION *conf, void **instance)
         */
        if (data->check_name == NULL) {
                radlog(L_ERR, "rlm_counter: 'check-name' must be set.");
-               exit(0);
+               counter_detach(data);
+               return -1;
        }
        dict_addattr(data->check_name, 0, PW_TYPE_INTEGER, -1, flags);
        dattr = dict_attrbyname(data->check_name);
        if (dattr == NULL) {
                radlog(L_ERR, "rlm_counter: Failed to create check attribute %s",
                                data->counter_name);
+               counter_detach(data);
                return -1;
        }
        data->check_attr = dattr->attr;
@@ -409,37 +447,44 @@ static int counter_instantiate(CONF_SECTION *conf, void **instance)
         * Find the attribute for the allowed protocol
         */
        if (data->service_type != NULL) {
-               if ((dval = dict_valbyname(PW_SERVICE_TYPE, data->service_type)) == NULL) {
+               if ((dval = dict_valbyname(PW_SERVICE_TYPE, 0, data->service_type)) == NULL) {
                        radlog(L_ERR, "rlm_counter: Failed to find attribute number for %s",
                                        data->service_type);
+                       counter_detach(data);
                        return -1;
                }
                data->service_val = dval->value;
-       }       
+       }
 
        /*
         * Find when to reset the database.
         */
        if (data->reset == NULL) {
                radlog(L_ERR, "rlm_counter: 'reset' must be set.");
-               exit(0);
+               counter_detach(data);
+               return -1;
        }
        now = time(NULL);
        data->reset_time = 0;
        data->last_reset = now;
 
-       if (find_next_reset(data,now) == -1)
+       if (find_next_reset(data,now) == -1){
+               radlog(L_ERR, "rlm_counter: find_next_reset() returned -1. Exiting.");
+               counter_detach(data);
                return -1;
+       }
 
        if (data->filename == NULL) {
                radlog(L_ERR, "rlm_counter: 'filename' must be set.");
-               exit(0);
+               counter_detach(data);
+               return -1;
        }
        data->gdbm = gdbm_open(data->filename, sizeof(int),
                        GDBM_WRCREAT | GDBM_COUNTER_OPTS, 0600, NULL);
        if (data->gdbm == NULL) {
                radlog(L_ERR, "rlm_counter: Failed to open file %s: %s",
                                data->filename, strerror(errno));
+               counter_detach(data);
                return -1;
        }
        if (gdbm_setopt(data->gdbm, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1)
@@ -458,7 +503,7 @@ static int counter_instantiate(CONF_SECTION *conf, void **instance)
         * If DEFAULT1 and DEFAULT2 do not exist (new database) we add them to the database
         */
 
-       key_datum.dptr = (const char *)default1;
+       key_datum.dptr = (char *)default1;
        key_datum.dsize = strlen(default1);
 
        time_datum = gdbm_fetch(data->gdbm, key_datum);
@@ -471,15 +516,18 @@ static int counter_instantiate(CONF_SECTION *conf, void **instance)
 
                        data->last_reset = now;
                        ret = reset_db(data);
-                       if (ret != RLM_MODULE_OK)
+                       if (ret != RLM_MODULE_OK){
+                               radlog(L_ERR, "rlm_counter: reset_db() failed");
+                               counter_detach(data);
                                return -1;
+                       }
                }
                else
                        data->reset_time = next_reset;
-               key_datum.dptr = (const char *)default2;
+               key_datum.dptr = (char *)default2;
                key_datum.dsize = strlen(default2);
 
-               time_datum = gdbm_fetch(data->gdbm, key_datum); 
+               time_datum = gdbm_fetch(data->gdbm, key_datum);
                if (time_datum.dptr != NULL){
                        memcpy(&data->last_reset, time_datum.dptr, sizeof(time_t));
                        free(time_datum.dptr);
@@ -487,8 +535,11 @@ static int counter_instantiate(CONF_SECTION *conf, void **instance)
        }
        else{
                ret = add_defaults(data);
-               if (ret != RLM_MODULE_OK)
+               if (ret != RLM_MODULE_OK){
+                       radlog(L_ERR, "rlm_counter: add_defaults() failed");
+                       counter_detach(data);
                        return -1;
+               }
        }
 
 
@@ -503,7 +554,7 @@ static int counter_instantiate(CONF_SECTION *conf, void **instance)
        pthread_mutex_init(&data->mutex, NULL);
 
        *instance = data;
-       
+
        return 0;
 }
 
@@ -521,8 +572,8 @@ static int counter_accounting(void *instance, REQUEST *request)
        int acctstatustype = 0;
        time_t diff;
 
-       if ((key_vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL)
-               acctstatustype = key_vp->lvalue;
+       if ((key_vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0)) != NULL)
+               acctstatustype = key_vp->vp_integer;
        else {
                DEBUG("rlm_counter: Could not find account status type in packet.");
                return RLM_MODULE_NOOP;
@@ -531,9 +582,9 @@ static int counter_accounting(void *instance, REQUEST *request)
                DEBUG("rlm_counter: We only run on Accounting-Stop packets.");
                return RLM_MODULE_NOOP;
        }
-       uniqueid_vp = pairfind(request->packet->vps, PW_ACCT_UNIQUE_SESSION_ID);
+       uniqueid_vp = pairfind(request->packet->vps, PW_ACCT_UNIQUE_SESSION_ID, 0);
        if (uniqueid_vp != NULL)
-               DEBUG("rlm_counter: Packet Unique ID = '%s'",uniqueid_vp->strvalue);
+               DEBUG("rlm_counter: Packet Unique ID = '%s'",uniqueid_vp->vp_strvalue);
 
        /*
         *      Before doing anything else, see if we have to reset
@@ -542,6 +593,7 @@ static int counter_accounting(void *instance, REQUEST *request)
        if (data->reset_time && (data->reset_time <= request->timestamp)) {
                int ret;
 
+               DEBUG("rlm_counter: Time to reset the database.");
                data->last_reset = data->reset_time;
                find_next_reset(data,request->timestamp);
                pthread_mutex_lock(&data->mutex);
@@ -554,11 +606,11 @@ static int counter_accounting(void *instance, REQUEST *request)
         * Check if we need to watch out for a specific service-type. If yes then check it
         */
        if (data->service_type != NULL) {
-               if ((proto_vp = pairfind(request->packet->vps, PW_SERVICE_TYPE)) == NULL){
+               if ((proto_vp = pairfind(request->packet->vps, PW_SERVICE_TYPE, 0)) == NULL){
                        DEBUG("rlm_counter: Could not find Service-Type attribute in the request. Returning NOOP.");
                        return RLM_MODULE_NOOP;
                }
-               if (proto_vp->lvalue != data->service_val){
+               if ((unsigned)proto_vp->vp_integer != data->service_val){
                        DEBUG("rlm_counter: This Service-Type is not allowed. Returning NOOP.");
                        return RLM_MODULE_NOOP;
                }
@@ -567,21 +619,22 @@ static int counter_accounting(void *instance, REQUEST *request)
         * Check if request->timestamp - {Acct-Delay-Time} < last_reset
         * If yes reject the packet since it is very old
         */
-       key_vp = pairfind(request->packet->vps, PW_ACCT_DELAY_TIME);
+       key_vp = pairfind(request->packet->vps, PW_ACCT_DELAY_TIME, 0);
        if (key_vp != NULL){
-               if (key_vp->lvalue != 0 && (request->timestamp - key_vp->lvalue) < data->last_reset){
+               if (key_vp->vp_integer != 0 &&
+                   (request->timestamp - key_vp->vp_integer) < data->last_reset){
                        DEBUG("rlm_counter: This packet is too old. Returning NOOP.");
                        return RLM_MODULE_NOOP;
                }
        }
 
-       
+
 
        /*
         *      Look for the key.  User-Name is special.  It means
         *      The REAL username, after stripping.
         */
-       key_vp = (data->key_attr == PW_USER_NAME) ? request->username : pairfind(request->packet->vps, data->key_attr);
+       key_vp = (data->key_attr == PW_USER_NAME) ? request->username : pairfind(request->packet->vps, data->key_attr, 0);
        if (key_vp == NULL){
                DEBUG("rlm_counter: Could not find the key-attribute in the request. Returning NOOP.");
                return RLM_MODULE_NOOP;
@@ -590,39 +643,44 @@ static int counter_accounting(void *instance, REQUEST *request)
        /*
         *      Look for the attribute to use as a counter.
         */
-       count_vp = pairfind(request->packet->vps, data->count_attr);
+       count_vp = pairfind(request->packet->vps, data->count_attr, 0);
        if (count_vp == NULL){
                DEBUG("rlm_counter: Could not find the count-attribute in the request.");
                return RLM_MODULE_NOOP;
        }
 
-       key_datum.dptr = key_vp->strvalue;
+       key_datum.dptr = key_vp->vp_strvalue;
        key_datum.dsize = key_vp->length;
 
+       DEBUG("rlm_counter: Searching the database for key '%s'",key_vp->vp_strvalue);
        pthread_mutex_lock(&data->mutex);
        count_datum = gdbm_fetch(data->gdbm, key_datum);
        pthread_mutex_unlock(&data->mutex);
        if (count_datum.dptr == NULL){
+               DEBUG("rlm_counter: Could not find the requested key in the database.");
                counter.user_counter = 0;
                if (uniqueid_vp != NULL)
-                       strncpy(uniqueid_vp->strvalue,counter.uniqueid,UNIQUEID_MAX_LEN - 1);
+                       strlcpy(counter.uniqueid,uniqueid_vp->vp_strvalue,
+                               sizeof(counter.uniqueid));
                else
                        memset((char *)counter.uniqueid,0,UNIQUEID_MAX_LEN);
        }
        else{
+               DEBUG("rlm_counter: Key found.");
                memcpy(&counter, count_datum.dptr, sizeof(rad_counter));
                free(count_datum.dptr);
                if (counter.uniqueid)
                        DEBUG("rlm_counter: Counter Unique ID = '%s'",counter.uniqueid);
-               if (uniqueid_vp != NULL){ 
-                       if (counter.uniqueid != NULL && 
-                               strncmp(uniqueid_vp->strvalue,counter.uniqueid, UNIQUEID_MAX_LEN - 1) == 0){
+               if (uniqueid_vp != NULL){
+                       if (counter.uniqueid != NULL &&
+                               strncmp(uniqueid_vp->vp_strvalue,counter.uniqueid, UNIQUEID_MAX_LEN - 1) == 0){
                                DEBUG("rlm_counter: Unique IDs for user match. Droping the request.");
                                return RLM_MODULE_NOOP;
                        }
-                       strncpy(counter.uniqueid,uniqueid_vp->strvalue,UNIQUEID_MAX_LEN - 1);
+                       strlcpy(counter.uniqueid,uniqueid_vp->vp_strvalue,
+                               sizeof(counter.uniqueid));
                }
-               DEBUG("rlm_counter: User=%s, Counter=%d.",request->username->strvalue,counter.user_counter);
+               DEBUG("rlm_counter: User=%s, Counter=%d.",request->username->vp_strvalue,counter.user_counter);
        }
 
        if (data->count_attr == PW_ACCT_SESSION_TIME) {
@@ -637,14 +695,14 @@ static int counter_accounting(void *instance, REQUEST *request)
                 *      day). That is the right thing
                 */
                diff = request->timestamp - data->last_reset;
-               counter.user_counter += (count_vp->lvalue < diff) ? count_vp->lvalue : diff;
+               counter.user_counter += (count_vp->vp_integer < diff) ? count_vp->vp_integer : diff;
 
        } else if (count_vp->type == PW_TYPE_INTEGER) {
                /*
                 *      Integers get counted, without worrying about
                 *      reset dates.
                 */
-               counter.user_counter += count_vp->lvalue;
+               counter.user_counter += count_vp->vp_integer;
 
        } else {
                /*
@@ -654,10 +712,11 @@ static int counter_accounting(void *instance, REQUEST *request)
                counter.user_counter++;
        }
 
-       DEBUG("rlm_counter: User=%s, New Counter=%d.",request->username->strvalue,counter.user_counter);
-       count_datum.dptr = (rad_counter *) &counter;
+       DEBUG("rlm_counter: User=%s, New Counter=%d.",request->username->vp_strvalue,counter.user_counter);
+       count_datum.dptr = (char *) &counter;
        count_datum.dsize = sizeof(rad_counter);
 
+       DEBUG("rlm_counter: Storing new value in database.");
        pthread_mutex_lock(&data->mutex);
        rcode = gdbm_store(data->gdbm, key_datum, count_datum, GDBM_REPLACE);
        pthread_mutex_unlock(&data->mutex);
@@ -666,6 +725,7 @@ static int counter_accounting(void *instance, REQUEST *request)
                                data->filename, gdbm_strerror(gdbm_errno));
                return RLM_MODULE_FAIL;
        }
+       DEBUG("rlm_counter: New value stored successfully.");
 
        return RLM_MODULE_OK;
 }
@@ -714,7 +774,7 @@ static int counter_authorize(void *instance, REQUEST *request)
         *      The REAL username, after stripping.
         */
        DEBUG2("rlm_counter: Entering module authorize code");
-       key_vp = (data->key_attr == PW_USER_NAME) ? request->username : pairfind(request->packet->vps, data->key_attr);
+       key_vp = (data->key_attr == PW_USER_NAME) ? request->username : pairfind(request->packet->vps, data->key_attr, 0);
        if (key_vp == NULL) {
                DEBUG2("rlm_counter: Could not find Key value pair");
                return ret;
@@ -723,12 +783,12 @@ static int counter_authorize(void *instance, REQUEST *request)
        /*
         *      Look for the check item
         */
-       if ((check_vp= pairfind(request->config_items, data->check_attr)) == NULL) {
+       if ((check_vp= pairfind(request->config_items, data->check_attr, 0)) == NULL) {
                DEBUG2("rlm_counter: Could not find Check item value pair");
                return ret;
        }
 
-       key_datum.dptr = key_vp->strvalue;
+       key_datum.dptr = key_vp->vp_strvalue;
        key_datum.dsize = key_vp->length;
 
 
@@ -737,23 +797,29 @@ static int counter_authorize(void *instance, REQUEST *request)
         */
 
        counter.user_counter = 0;
-       
+
+       DEBUG("rlm_counter: Searching the database for key '%s'",key_vp->vp_strvalue);
        pthread_mutex_lock(&data->mutex);
        count_datum = gdbm_fetch(data->gdbm, key_datum);
        pthread_mutex_unlock(&data->mutex);
        if (count_datum.dptr != NULL){
+               DEBUG("rlm_counter: Key Found.");
                memcpy(&counter, count_datum.dptr, sizeof(rad_counter));
                free(count_datum.dptr);
        }
+       else
+               DEBUG("rlm_counter: Could not find the requested key in the database.");
 
        /*
         * Check if check item > counter
         */
-       res=check_vp->lvalue - counter.user_counter;
+       DEBUG("rlm_counter: Check item = %d, Count = %d",check_vp->vp_integer,counter.user_counter);
+       res=check_vp->vp_integer - counter.user_counter;
        if (res > 0) {
+               DEBUG("rlm_counter: res is greater than zero");
                if (data->count_attr == PW_ACCT_SESSION_TIME) {
                        /*
-                        * Do the following only if the count attribute is 
+                        * Do the following only if the count attribute is
                         * AcctSessionTime
                         */
 
@@ -773,22 +839,31 @@ static int counter_authorize(void *instance, REQUEST *request)
                        *       If we are near a reset then add the next
                        *       limit, so that the user will not need to
                        *       login again
+                       *       Before that set the return value to the time
+                       *       remaining to next reset
                        */
                        if (data->reset_time && (
                                res >= (data->reset_time - request->timestamp))) {
-                               res += check_vp->lvalue;
+                               res = data->reset_time - request->timestamp;
+                               res += check_vp->vp_integer;
                        }
 
-                       if ((reply_item = pairfind(request->reply->vps, PW_SESSION_TIMEOUT)) != NULL) {
-                               if (reply_item->lvalue > res)
-                                       reply_item->lvalue = res;
+                       if ((reply_item = pairfind(request->reply->vps, PW_SESSION_TIMEOUT, 0)) != NULL) {
+                               if (reply_item->vp_integer > res)
+                                       reply_item->vp_integer = res;
                        } else {
-                               if ((reply_item = paircreate(PW_SESSION_TIMEOUT, PW_TYPE_INTEGER)) == NULL) {
-                                       radlog(L_ERR|L_CONS, "no memory");
-                                       return RLM_MODULE_NOOP;
-                               }
-                               reply_item->lvalue = res;
-                               pairadd(&request->reply->vps, reply_item);
+                               reply_item = radius_paircreate(request, &request->reply->vps, PW_SESSION_TIMEOUT, 0, PW_TYPE_INTEGER);
+                               reply_item->vp_integer = res;
+                       }
+               }
+               else if (data->reply_attr) {
+                       if ((reply_item = pairfind(request->reply->vps, data->reply_attr, 0)) != NULL) {
+                               if (reply_item->vp_integer > res)
+                                       reply_item->vp_integer = res;
+                       }
+                       else {
+                               reply_item = radius_paircreate(request, &request->reply->vps, data->reply_attr, 0, PW_TYPE_INTEGER);
+                               reply_item->vp_integer = res;
                        }
                }
 
@@ -796,9 +871,9 @@ static int counter_authorize(void *instance, REQUEST *request)
 
                DEBUG2("rlm_counter: (Check item - counter) is greater than zero");
                DEBUG2("rlm_counter: Authorized user %s, check_item=%d, counter=%d",
-                               key_vp->strvalue,check_vp->lvalue,counter.user_counter);
+                               key_vp->vp_strvalue,check_vp->vp_integer,counter.user_counter);
                DEBUG2("rlm_counter: Sent Reply-Item for user %s, Type=Session-Timeout, value=%d",
-                               key_vp->strvalue,res);
+                               key_vp->vp_strvalue,res);
        }
        else{
                char module_fmsg[MAX_STRING_LEN];
@@ -813,12 +888,12 @@ static int counter_authorize(void *instance, REQUEST *request)
 
                snprintf(module_fmsg,sizeof(module_fmsg), "rlm_counter: Maximum %s usage time reached", data->reset);
                module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
-               pairadd(&request->packet->vps, module_fmsg_vp); 
+               pairadd(&request->packet->vps, module_fmsg_vp);
 
                ret=RLM_MODULE_REJECT;
 
                DEBUG2("rlm_counter: Rejected user %s, check_item=%d, counter=%d",
-                               key_vp->strvalue,check_vp->lvalue,counter.user_counter);
+                               key_vp->vp_strvalue,check_vp->vp_integer,counter.user_counter);
        }
 
        return ret;
@@ -829,12 +904,8 @@ static int counter_detach(void *instance)
        rlm_counter_t *data = (rlm_counter_t *) instance;
 
        paircompare_unregister(data->dict_attr, counter_cmp);
-       gdbm_close(data->gdbm);
-       free(data->filename);
-       free(data->reset);
-       free(data->key_name);
-       free(data->count_attribute);
-       free(data->counter_name);
+       if (data->gdbm)
+               gdbm_close(data->gdbm);
        pthread_mutex_destroy(&data->mutex);
 
        free(instance);
@@ -851,10 +922,11 @@ static int counter_detach(void *instance)
  *     is single-threaded.
  */
 module_t rlm_counter = {
-       "Counter",      
+        RLM_MODULE_INIT,
+       "counter",
        RLM_TYPE_THREAD_SAFE,           /* type */
-       NULL,                           /* initialization */
        counter_instantiate,            /* instantiation */
+       counter_detach,                 /* detach */
        {
                NULL,                   /* authentication */
                counter_authorize,      /* authorization */
@@ -865,6 +937,4 @@ module_t rlm_counter = {
                NULL,                   /* post-proxy */
                NULL                    /* post-auth */
        },
-       counter_detach,                 /* detach */
-       NULL,                           /* destroy */
 };