📄 rlm_counter.c
字号:
/* * rlm_counter.c * * Version: $Id: rlm_counter.c,v 1.43 2004/02/26 19:04:28 aland Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * 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 * * Copyright 2001 The FreeRADIUS server project * Copyright 2001 Alan DeKok <aland@ox.org> * Copyright 2001-3 Kostas Kalevras <kkalev@noc.ntua.gr> */#include "config.h"#include "autoconf.h"#include "libradius.h"#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include "radiusd.h"#include "modules.h"#include "conffile.h"#include <gdbm.h>#include <time.h>#ifdef NEEDS_GDBM_SYNC# define GDBM_SYNCOPT GDBM_SYNC#else# define GDBM_SYNCOPT 0#endif#ifdef GDBM_NOLOCK#define GDBM_COUNTER_OPTS (GDBM_SYNCOPT | GDBM_NOLOCK)#else#define GDBM_COUNTER_OPTS (GDBM_SYNCOPT)#endif#ifndef HAVE_GDBM_FDESC#define gdbm_fdesc(foo) (-1)#endif#define UNIQUEID_MAX_LEN 32static const char rcsid[] = "$Id: rlm_counter.c,v 1.43 2004/02/26 19:04:28 aland Exp $";/* * Define a structure for our module configuration. * * These variables do not need to be in a structure, but it's * a lot cleaner to do so, and a pointer to the structure can * be used as the instance handle. */typedef struct rlm_counter_t { char *filename; /* name of the database file */ char *reset; /* daily, weekly, monthly, never or user defined */ char *key_name; /* User-Name */ char *count_attribute; /* Acct-Session-Time */ char *counter_name; /* Daily-Session-Time */ char *check_name; /* Daily-Max-Session */ char *service_type; /* Service-Type to search for */ int cache_size; int service_val; int key_attr; int count_attr; int check_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. */ GDBM_FILE gdbm; /* The gdbm file handle */#ifdef HAVE_PTHREAD_H pthread_mutex_t mutex; /* A mutex to lock the gdbm file for only one reader/writer */#endif} rlm_counter_t;#ifndef HAVE_PTHREAD_H/* * This is a lot simpler than putting ifdef's around * every use of the pthread functions. */#define pthread_mutex_lock(a)#define pthread_mutex_unlock(a)#define pthread_mutex_init(a,b)#define pthread_mutex_destroy(a)#endiftypedef struct rad_counter { unsigned int user_counter; char uniqueid[UNIQUEID_MAX_LEN];} rad_counter;/* * A mapping of configuration file names to internal variables. * * Note that the string is dynamically allocated, so it MUST * be freed. When the configuration file parse re-reads the string, * it free's the old one, and strdup's the new one, placing the pointer * to the strdup'd string into 'config.string'. This gets around * buffer over-flows. */static 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 }, { "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 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; datum count_datum; VALUE_PAIR *key_vp; rad_counter counter; 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); if (key_vp == NULL) { return RLM_MODULE_NOOP; } key_datum.dptr = key_vp->strvalue; key_datum.dsize = key_vp->length; count_datum = gdbm_fetch(data->gdbm, key_datum); if (count_datum.dptr == NULL) { return -1; } memcpy(&counter, count_datum.dptr, sizeof(rad_counter)); free(count_datum.dptr); return counter.user_counter - check->lvalue;}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 = (char *) default1; key_datum.dsize = strlen(default1); time_datum.dptr = (char *) &data->reset_time; time_datum.dsize = sizeof(time_t); if (gdbm_store(data->gdbm, key_datum, time_datum, GDBM_REPLACE) < 0){ radlog(L_ERR, "rlm_counter: Failed storing data to %s: %s", data->filename, gdbm_strerror(gdbm_errno)); return RLM_MODULE_FAIL; } DEBUG2("rlm_counter: DEFAULT1 set to %d",(int)data->reset_time); key_datum.dptr = (char *) default2; key_datum.dsize = strlen(default2); time_datum.dptr = (char *) &data->last_reset; time_datum.dsize = sizeof(time_t); if (gdbm_store(data->gdbm, key_datum, time_datum, GDBM_REPLACE) < 0){ radlog(L_ERR, "rlm_counter: Failed storing data to %s: %s", data->filename, gdbm_strerror(gdbm_errno)); return RLM_MODULE_FAIL; } DEBUG2("rlm_counter: DEFAULT2 set to %d",(int)data->last_reset); DEBUG2("rlm_counter: add_defaults: End"); return RLM_MODULE_OK;}static int reset_db(rlm_counter_t *data){ int cache_size = data->cache_size; int ret; DEBUG2("rlm_counter: reset_db: Closing database"); gdbm_close(data->gdbm); /* * Open a completely new database. */ data->gdbm = gdbm_open(data->filename, sizeof(int), GDBM_NEWDB | GDBM_COUNTER_OPTS, 0600, NULL); if (data->gdbm == NULL) { radlog(L_ERR, "rlm_counter: Failed to open file %s: %s", data->filename, strerror(errno)); return RLM_MODULE_FAIL; } if (gdbm_setopt(data->gdbm, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1) radlog(L_ERR, "rlm_counter: Failed to set cache size"); DEBUG2("rlm_counter: reset_db: Opened new database"); /* * Add defaults */ ret = add_defaults(data); if (ret != RLM_MODULE_OK) return ret; DEBUG2("rlm_counter: reset_db ended"); return RLM_MODULE_OK;}static int find_next_reset(rlm_counter_t *data, time_t timeval){ int ret=0; unsigned int num=1; char last = 0; struct tm *tm, s_tm; char sCurrentTime[40], sNextTime[40]; tm = localtime_r(&timeval, &s_tm); strftime(sCurrentTime, sizeof(sCurrentTime),"%Y-%m-%d %H:%M:%S",tm); 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; last = data->reset[len - 1]; if (!isalpha((int) last)) last = 'd'; num = atoi(data->reset); DEBUG("rlm_counter: num=%d, last=%c",num,last); } if (strcmp(data->reset, "hourly") == 0 || last == 'h') { /* * Round up to the next nearest hour. */ tm->tm_hour += num; data->reset_time = mktime(tm); } else if (strcmp(data->reset, "daily") == 0 || last == 'd') { /* * Round up to the next nearest day. */ tm->tm_hour = 0; tm->tm_mday += num; data->reset_time = mktime(tm); } else if (strcmp(data->reset, "weekly") == 0 || last == 'w') { /* * Round up to the next nearest week. */ tm->tm_hour = 0; tm->tm_mday += (7 - tm->tm_wday) +(7*(num-1)); data->reset_time = mktime(tm); } else if (strcmp(data->reset, "monthly") == 0 || last == 'm') { tm->tm_hour = 0; tm->tm_mday = 1; tm->tm_mon += num; data->reset_time = mktime(tm); } else if (strcmp(data->reset, "never") == 0) { data->reset_time = 0; } else { radlog(L_ERR, "rlm_counter: Unknown reset timer \"%s\"", data->reset); return -1; } strftime(sNextTime, sizeof(sNextTime),"%Y-%m-%d %H:%M:%S",tm); DEBUG2("rlm_counter: Current Time: %d [%s], Next reset %d [%s]", (int)timeval,sCurrentTime,(int)data->reset_time,sNextTime); return ret;}/* * Do any per-module initialization that is separate to each * configured instance of the module. e.g. set up connections * to external databases, read configuration files, set up * dictionary entries, etc. * * If configuration information is given in the config section * that must be referenced in later calls, store a handle to it * in *instance otherwise put a null pointer there. */static int counter_instantiate(CONF_SECTION *conf, void **instance){ rlm_counter_t *data; DICT_ATTR *dattr; DICT_VALUE *dval; ATTR_FLAGS flags; time_t now; int cache_size; int ret; datum key_datum; 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)); /* * If the configuration parameters can't be parsed, then * fail. */ if (cf_section_parse(conf, data, module_config) < 0) { free(data); return -1; } cache_size = data->cache_size; /* * Discover the attribute number of the key. */ if (data->key_name == NULL) { radlog(L_ERR, "rlm_counter: 'key' must be set."); 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. */ if (data->count_attribute == NULL) { radlog(L_ERR, "rlm_counter: 'count-attribute' must be set."); 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; /* * Create a new attribute for the counter. */ if (data->counter_name == NULL) { radlog(L_ERR, "rlm_counter: 'counter-name' must be set."); counter_detach(data); return -1; } memset(&flags, 0, sizeof(flags)); dict_addattr(data->counter_name, 0, PW_TYPE_INTEGER, -1, 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; DEBUG2("rlm_counter: Counter attribute %s is number %d", data->counter_name, data->dict_attr); /* * Create a new attribute for the check item. */ if (data->check_name == NULL) { radlog(L_ERR, "rlm_counter: 'check-name' must be set."); 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; /* * Find the attribute for the allowed protocol */ if (data->service_type != NULL) { if ((dval = dict_valbyname(PW_SERVICE_TYPE, 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."); counter_detach(data); return -1; } now = time(NULL); data->reset_time = 0; data->last_reset = now; 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."); counter_detach(data);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -