⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rlm_caching.c

📁 使用最广泛的radius的linux的源码
💻 C
字号:
/* * rlm_caching.c * * Version:  $Id: rlm_caching.c,v 1.12 2007/05/14 22:27:08 nbk 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * Copyright 2001,2006  The FreeRADIUS server project * Copyright 2001  Alan DeKok <aland@ox.org> * Copyright 2001-3  Kostas Kalevras <kkalev@noc.ntua.gr> */#include <freeradius-devel/ident.h>RCSID("$Id: rlm_caching.c,v 1.12 2007/05/14 22:27:08 nbk Exp $")#include <freeradius-devel/radiusd.h>#include <freeradius-devel/modules.h>#include <ctype.h>#include "config.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 32/* *	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_caching_t {	char *filename;		/* name of the database file */	char *key;		/* An xlated string to use as key for the records */	char *post_auth;	/* If set and we find a cached entry, set Post-Auth to this value */	char *cache_ttl_str;	/* The string represantation of the TTL */	int cache_ttl;		/* The cache TTL */	int hit_ratio;		/* Show cache hit ratio every so many queries */	int cache_rejects;	/* Do we also cache rejects? */	int cache_size;		/* The cache size to pass to GDBM */	uint32_t cache_queries;	/* The number of cache requests */	uint32_t cache_hits;	/* The number of cache hits */	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_caching_t;#define MAX_RECORD_LEN 750#define MAX_AUTH_TYPE 32#define show_hit_ratio \	if (data->hit_ratio && (data->cache_queries % data->hit_ratio) == 0) \		radlog(L_INFO, "rlm_caching: Cache Queries: %7d, Cache Hits: %7d, Hit Ratio: %.2f%%", \			data->cache_queries,data->cache_hits,hit_ratio)typedef struct rlm_caching_data {	time_t creation;	char data[MAX_RECORD_LEN];	char auth_type[MAX_AUTH_TYPE];	int len;} rlm_caching_data;#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)#endif/* *	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 const CONF_PARSER module_config[] = {  { "filename", PW_TYPE_STRING_PTR, offsetof(rlm_caching_t,filename), NULL, NULL },  { "key", PW_TYPE_STRING_PTR, offsetof(rlm_caching_t,key), NULL, "%{Acct-Unique-Session-Id}" },  { "post-auth", PW_TYPE_STRING_PTR, offsetof(rlm_caching_t,post_auth), NULL,  NULL },  { "cache-ttl", PW_TYPE_STRING_PTR, offsetof(rlm_caching_t,cache_ttl_str), NULL, "1d" },  { "cache-size", PW_TYPE_INTEGER, offsetof(rlm_caching_t,cache_size), NULL, "1000" },  { "hit-ratio", PW_TYPE_INTEGER, offsetof(rlm_caching_t,hit_ratio), NULL, "0" },  { "cache-rejects", PW_TYPE_BOOLEAN, offsetof(rlm_caching_t,cache_rejects), NULL, "yes" },  { NULL, -1, 0, NULL, NULL }};static int caching_detach(void *instance);static int find_ttl(char *ttl){	unsigned len = 0;	char last = 's';	if (isdigit((int) ttl[0])){		len = strlen(ttl);		if (len == 0)			return -1;		last = ttl[len - 1];		if (!isalpha((int) last))			last = 's';		len = atoi(ttl);		DEBUG("rlm_caching::find_ttl: num=%d, last=%c",len,last);	}	switch (last){		case 's':		default:			break;		case 'm':			len *= 60;			break;		case 'h':			len *= 3600;			break;		case 'd':			len *= 86400;			break;		case 'w':			len *= 604800;			break;	}	DEBUG("rlm_caching::find_ttl: Returning '%d'",len);	return len;}/* *	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 caching_instantiate(CONF_SECTION *conf, void **instance){	rlm_caching_t *data;	int cache_size;	/*	 *	Set up a storage area for instance data	 */	data = rad_malloc(sizeof(*data));	if (!data) {		radlog(L_ERR, "rlm_caching: 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 == NULL) {		radlog(L_ERR, "rlm_caching: 'key' must be set.");		caching_detach(data);		return -1;	}	if (data->cache_ttl_str == NULL) {		radlog(L_ERR, "rlm_caching: 'cache-ttl' must be set.");		caching_detach(data);		return -1;	}	else {		data->cache_ttl = find_ttl(data->cache_ttl_str);		if (data->cache_ttl == 0) {			radlog(L_ERR, "rlm_caching: 'cache-ttl' is invalid.");			caching_detach(data);			return -1;		}	}	if (data->filename == NULL) {		radlog(L_ERR, "rlm_caching: 'filename' must be set.");		caching_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_caching: Failed to open file %s: %s",				data->filename, strerror(errno));		caching_detach(data);		return -1;	}	if (gdbm_setopt(data->gdbm, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1)		radlog(L_ERR, "rlm_caching: Failed to set cache size");	/*	 * Init the mutex	 */	pthread_mutex_init(&data->mutex, NULL);	*instance = data;	return 0;}/* *	Cache the reply items and the Auth-Type */static int caching_postauth(void *instance, REQUEST *request){	rlm_caching_t *data = (rlm_caching_t *)instance;	char key[MAX_STRING_LEN];	datum key_datum;	datum data_datum;	VALUE_PAIR *reply_vp;	VALUE_PAIR *auth_type;	rlm_caching_data cache_data;	int count = 0;	int ret = 0;	int size = 0;	int rcode = 0;	if (pairfind(request->packet->vps, PW_CACHE_NO_CACHING) != NULL){		DEBUG("rlm_caching: Cache-No-Caching is set. Returning NOOP");		return RLM_MODULE_NOOP;	}	if ((auth_type = pairfind(request->config_items, PW_AUTH_TYPE)) != NULL){		DEBUG("rlm_caching: Found Auth-Type, value: '%s'",auth_type->vp_strvalue);		if (strcmp(auth_type->vp_strvalue,"Reject") == 0 && data->cache_rejects == 0){			DEBUG("rlm_caching: No caching of Rejects. Returning NOOP");			return RLM_MODULE_NOOP;		}		if (strlen(auth_type->vp_strvalue) > MAX_AUTH_TYPE - 1){			DEBUG("rlm_caching: Auth-Type value too large");			return RLM_MODULE_NOOP;		}	}	else{		DEBUG("rlm_caching: No Auth-Type found. Returning NOOP");		return RLM_MODULE_NOOP;	}	reply_vp = request->reply->vps;	if (reply_vp == NULL) {		DEBUG("rlm_caching: The Request does not contain any reply attributes");		return RLM_MODULE_NOOP;	}	if (!radius_xlat(key,sizeof(key), data->key, request, NULL)){		radlog(L_ERR, "rlm_caching: xlat on key '%s' failed.",data->key);		return RLM_MODULE_FAIL;	}	memset(&cache_data,0,sizeof(rlm_caching_data));	cache_data.creation = time(NULL);	strcpy(cache_data.auth_type,auth_type->vp_strvalue);	size = MAX_RECORD_LEN;	while(reply_vp) {		if (size <= 1){			DEBUG("rlm_caching: Not enough space.");			return RLM_MODULE_NOOP;		}		ret = vp_prints(cache_data.data + count,size,reply_vp);		if (ret == 0) {			DEBUG("rlm_caching: Record is too large, will not store it.");			return RLM_MODULE_NOOP;		}		count += (ret + 1);		size -= (ret + 1);		DEBUG("rlm_caching: VP=%s,VALUE=%s,length=%d,cache record length=%d, space left=%d",			reply_vp->name,reply_vp->vp_strvalue,ret,count,size);		reply_vp = reply_vp->next;	}	cache_data.len = count;	DEBUG("rlm_caching: Storing cache for Key='%s'",key);	data_datum.dptr = (rlm_caching_data *) &cache_data;	data_datum.dsize = sizeof(rlm_caching_data);	key_datum.dptr = (char *) key;	key_datum.dsize = strlen(key);	pthread_mutex_lock(&data->mutex);	rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);	pthread_mutex_unlock(&data->mutex);	if (rcode < 0) {		radlog(L_ERR, "rlm_caching: Failed storing data to %s: %s",				data->filename, gdbm_strerror(gdbm_errno));		return RLM_MODULE_FAIL;	}	DEBUG("rlm_caching: New value stored successfully.");	return RLM_MODULE_OK;}/* *	Find the named user in this modules database.  Create the set *	of attribute-value pairs to check and reply with for this user *	from the database. The authentication code only needs to check *	the password, the rest is done here. */static int caching_authorize(void *instance, REQUEST *request){	rlm_caching_t *data = (rlm_caching_t *) instance;	char key[MAX_STRING_LEN];	datum key_datum;	datum data_datum;	rlm_caching_data cache_data;	VALUE_PAIR *reply_item;	VALUE_PAIR *item;	char *tmp;	int len = 0;	int delete_cache = 0;	float hit_ratio = 0.0;	/* quiet the compiler */	instance = instance;	request = request;	if (pairfind(request->packet->vps, PW_CACHE_NO_CACHING) != NULL){		DEBUG("rlm_caching: Cache-No-Caching is set. Returning NOOP");		return RLM_MODULE_NOOP;	}	if (pairfind(request->packet->vps, PW_CACHE_DELETE_CACHE) != NULL){		DEBUG("rlm_caching: Found Cache-Delete-Cache. Will delete record if found");		delete_cache = 1;	}	if (!radius_xlat(key,sizeof(key), data->key, request, NULL)){		radlog(L_ERR, "rlm_caching: xlat on key '%s' failed.",data->key);		return RLM_MODULE_FAIL;	}	key_datum.dptr = key;	key_datum.dsize = strlen(key);	DEBUG("rlm_caching: Searching the database for key '%s'",key);	pthread_mutex_lock(&data->mutex);	data_datum = gdbm_fetch(data->gdbm, key_datum);	pthread_mutex_unlock(&data->mutex);	data->cache_queries++;	if (data_datum.dptr != NULL){		DEBUG("rlm_caching: Key Found.");		data->cache_hits++;		hit_ratio = (float)data->cache_hits / data->cache_queries;		hit_ratio *= 100.0;		memcpy(&cache_data, data_datum.dptr, sizeof(rlm_caching_data));		free(data_datum.dptr);		if (delete_cache == 0 && cache_data.creation + data->cache_ttl <= time(NULL)){			DEBUG("rlm_caching: Cache entry has expired");			DEBUG("rlm_caching: Cache Queries: %7d, Cache Hits: %7d, Hit Ratio: %.2f%%",			data->cache_queries,data->cache_hits,hit_ratio);			show_hit_ratio;			delete_cache = 1;		}		if (delete_cache){			DEBUG("rlm_caching: Deleting record");			pthread_mutex_lock(&data->mutex);			gdbm_delete(data->gdbm, key_datum);			pthread_mutex_unlock(&data->mutex);			return RLM_MODULE_NOOP;		}		tmp = cache_data.data;		if (tmp){			pairfree(&request->reply->vps);			while(tmp && len < cache_data.len){				reply_item = NULL;				if (userparse(tmp, &reply_item) > 0 && reply_item != NULL)					pairadd(&request->reply->vps, reply_item);				len += (strlen(tmp) + 1);				DEBUG("rlm_caching: VP='%s',VALUE='%s',lenth='%d',cache record length='%d'",				reply_item->name,reply_item->vp_strvalue,reply_item->length,len);				tmp = cache_data.data + len;			}		}		else{			DEBUG("rlm_caching: No reply items found. Returning NOOP");			return RLM_MODULE_NOOP;		}		if (cache_data.auth_type){			DEBUG("rlm_caching: Adding Auth-Type '%s'",cache_data.auth_type);			if ((item = pairfind(request->config_items, PW_AUTH_TYPE)) == NULL){				item = pairmake("Auth-Type", cache_data.auth_type, T_OP_SET);				pairadd(&request->config_items, item);			}			else{				strcmp(item->vp_strvalue, cache_data.auth_type);				item->length = strlen(cache_data.auth_type);			}		}		if (data->post_auth){			DEBUG("rlm_caching: Adding Post-Auth-Type '%s'",data->post_auth);			if ((item = pairfind(request->config_items, PW_POST_AUTH_TYPE)) == NULL){				item = pairmake("Post-Auth-Type", data->post_auth, T_OP_SET);				pairadd(&request->config_items, item);			}			else{				strcmp(item->vp_strvalue, data->post_auth);				item->length = strlen(data->post_auth);			}		}		item = pairmake("Cache-No-Caching", "YES", T_OP_EQ);		pairadd(&request->packet->vps, item);		DEBUG("rlm_caching: Cache Queries: %7d, Cache Hits: %7d, Hit Ratio: %.2f%%",			data->cache_queries,data->cache_hits,hit_ratio);		show_hit_ratio;		return RLM_MODULE_OK;	}	else{		DEBUG("rlm_caching: Could not find the requested key in the database.");		DEBUG("rlm_caching: Cache Queries: %7d, Cache Hits: %7d, Hit Ratio: %.2f%%",			data->cache_queries,data->cache_hits,hit_ratio);		show_hit_ratio;	}	return RLM_MODULE_NOOP;}static int caching_detach(void *instance){	rlm_caching_t *data = (rlm_caching_t *) instance;	if (data->gdbm)		gdbm_close(data->gdbm);	pthread_mutex_destroy(&data->mutex);	free(instance);	return 0;}/* *	The module name should be the only globally exported symbol. *	That is, everything else should be 'static'. * *	If the module needs to temporarily modify it's instantiation *	data, the type should be changed to RLM_TYPE_THREAD_UNSAFE. *	The server will then take care of ensuring that the module *	is single-threaded. */module_t rlm_caching = {	RLM_MODULE_INIT,	"Caching",	RLM_TYPE_THREAD_SAFE,		/* type */	caching_instantiate,		/* instantiation */	caching_detach,			/* detach */	{		NULL,			/* authentication */		caching_authorize, 	/* authorization */		NULL,			/* preaccounting */		NULL,			/* accounting */		NULL,			/* checksimul */		NULL,			/* pre-proxy */		NULL,			/* post-proxy */		caching_postauth	/* post-auth */	},};

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -