📄 rlm_radutmp2.c
字号:
/* * rlm_radutmp.c * * Version: $Id$ * * 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 2000,2001,2002,2003,2004,2006 The FreeRADIUS server project */#include <freeradius-devel/ident.h>RCSID("$Id$")#include <freeradius-devel/radiusd.h>#include <freeradius-devel/radutmp.h>#include <freeradius-devel/modules.h>#include <freeradius-devel/rad_assert.h>#include <fcntl.h>#include <limits.h>#include "config.h"#define LOCK_LEN sizeof(struct radutmp)static const char porttypes[] = "ASITX";/* * Used for caching radutmp lookups in the accounting * component. The session (checksimul) component doesn't use it, * but probably should, though we're not sure how... * * The intent here is to keep this structure as small as * possible, so that it doesn't take up too much memory. */typedef struct nas_port { uint32_t nas_address; unsigned int nas_port; off_t offset; struct nas_port *next; /* for the free list */} NAS_PORT;/* * Per-file information. * * Hmm... having multiple filenames managed by one instance * of the module makes it difficult for the module to do * simultaneous-use checking, without more code edits. */typedef struct radutmp_cache_t { const char *filename; /* for future reference */ time_t last_used; /* for future reference */ rbtree_t *nas_ports; NAS_PORT *free_offsets; off_t max_offset; int cached_file; int permission;#ifdef HAVE_PTHREAD_H pthread_mutex_t mutex;#endif} radutmp_cache_t;/* * We cache the users, too, so that we only have to read radutmp * once. */typedef struct radutmp_simul_t { char login[sizeof(((struct radutmp *) NULL)->login) + 1]; int simul_count;} radutmp_simul_t;/* * Data we store per module. */typedef struct rlm_radutmp_t { char *filename; char *username; int case_sensitive; int check_nas; int permission; int callerid_ok; rbtree_t *user_tree; /* for simultaneous-use */ /* * As the filenames can be dynamically translated, * we want to keep track of them in a separate data * structure, so that we can have per-file caches. */ radutmp_cache_t cache;} rlm_radutmp_t;#ifndef HAVE_PTHREAD_H/* * This is easier than ifdef's throughout the code. */#define pthread_mutex_init(_x, _y)#define pthread_mutex_destroy(_x)#define pthread_mutex_lock(_x)#define pthread_mutex_unlock(_x)#endifstatic const CONF_PARSER module_config[] = { { "filename", PW_TYPE_STRING_PTR, offsetof(rlm_radutmp_t,filename), NULL, RADUTMP }, { "username", PW_TYPE_STRING_PTR, offsetof(rlm_radutmp_t,username), NULL, "%{User-Name}"}, { "case_sensitive", PW_TYPE_BOOLEAN, offsetof(rlm_radutmp_t,case_sensitive), NULL, "yes"}, { "check_with_nas", PW_TYPE_BOOLEAN, offsetof(rlm_radutmp_t,check_nas), NULL, "yes"}, { "perm", PW_TYPE_INTEGER, offsetof(rlm_radutmp_t,permission), NULL, "0644" }, { "callerid", PW_TYPE_BOOLEAN, offsetof(rlm_radutmp_t,callerid_ok), NULL, "no" }, { NULL, -1, 0, NULL, NULL } /* end the list */};/* * NAS PORT cmp */static int nas_port_cmp(const void *a, const void *b){ const NAS_PORT *one = a; const NAS_PORT *two = b; if (one->nas_address < two->nas_address) return -1; if (one->nas_address > two->nas_address) return +1; if (one->nas_port < two->nas_port) return -1; if (one->nas_port > two->nas_port) return +1; return 0;}/* * Compare two user names. */static int user_cmp(const void *a, const void *b){ const radutmp_simul_t *one = a; const radutmp_simul_t *two = b; return strcmp(one->login, two->login);}/* * Compare two user names, case insensitive. */static int user_case_cmp(const void *a, const void *b){ const radutmp_simul_t *one = a; const radutmp_simul_t *two = b; return strcasecmp(one->login, two->login);}/* * Detach. */static int radutmp_detach(void *instance){ NAS_PORT *this, *next; rlm_radutmp_t *inst = instance; rbtree_free(inst->cache.nas_ports); for (this = inst->cache.free_offsets; this != NULL; this = next) { next = this->next; free(this); } if (inst->cache.filename) free(inst->cache.filename); pthread_mutex_destroy(&(inst->cache.mutex)); rbtree_free(inst->user_tree); free(inst); return 0;}/* * Instantiate. */static int radutmp_instantiate(CONF_SECTION *conf, void **instance){ rlm_radutmp_t *inst; inst = rad_malloc(sizeof(*inst)); if (!inst) { return -1; } memset(inst, 0, sizeof(*inst)); if (cf_section_parse(conf, inst, module_config)) { radutmp_detach(inst); return -1; } inst->cache.nas_ports = rbtree_create(nas_port_cmp, free, 0); if (!inst->cache.nas_ports) { radlog(L_ERR, "rlm_radutmp: Failed to create nas tree"); radutmp_detach(inst); return -1; } pthread_mutex_init(&(inst->cache.mutex), NULL); inst->cache.permission = inst->permission; if (inst->case_sensitive) { inst->user_tree = rbtree_create(user_cmp, free, 0); } else { inst->user_tree = rbtree_create(user_case_cmp, free, 0); } if (!inst->user_tree) { radlog(L_ERR, "rlm_radutmp: Failed to create user tree"); radutmp_detach(inst); return -1; } *instance = inst; return 0;}/* * Reset the cached entries. */static int cache_reset(rlm_radutmp_t *inst, radutmp_cache_t *cache){ NAS_PORT *this, *next; /* * Cache is already reset, do nothing. */ if ((rbtree_num_elements(cache->nas_ports) == 0) && (cache->free_offsets == NULL)) { DEBUG2(" rlm_radutmp: Not resetting the cache"); return 1; } DEBUG2(" rlm_radutmp: Resetting the cache"); pthread_mutex_lock(&cache->mutex); rbtree_free(inst->user_tree); rbtree_free(cache->nas_ports); for (this = cache->free_offsets; this != NULL; this = next) { next = this->next; free(this); } cache->free_offsets = NULL; /* * Re-create the caches. */ cache->nas_ports = rbtree_create(nas_port_cmp, free, 0); if (!cache->nas_ports) { pthread_mutex_unlock(&cache->mutex); radlog(L_ERR, "rlm_radutmp: No memory"); return 0; } cache->max_offset = 0; cache->cached_file = 1; if (inst->case_sensitive) { inst->user_tree = rbtree_create(user_cmp, free, 0); } else { inst->user_tree = rbtree_create(user_case_cmp, free, 0); } if (!inst->user_tree) { pthread_mutex_unlock(&cache->mutex); radlog(L_ERR, "rlm_radutmp: No memory"); return 0; } pthread_mutex_unlock(&cache->mutex); return 1;}/* * Compare two offsets in a tree. */static int offset_cmp(const void *a, const void *b){ const NAS_PORT *one = a; const NAS_PORT *two = b; if (one->offset < two->offset) return -1; if (one->offset > two->offset) return +1; return 0;}/* * Data structure to use when walking the trees, for zap. */typedef struct offset_walk_t { rlm_radutmp_t *inst; radutmp_cache_t *cache; rbtree_t *offset_tree; uint32_t nas_address; int fd; time_t now;} offset_walk_t;/* * Walk over the cache, finding entries with the matching NAS IP address. */static int nas_port_walk(void *context, void *data){ offset_walk_t *walk = context; NAS_PORT *nas_port = data; /* * Doesn't match, keep going. */ if (walk->nas_address != nas_port->nas_address) return 0; /* * Insert it into the offset tree, for later deletion. */ if (rbtree_insert(walk->offset_tree, nas_port) != 1) { DEBUG2(" rlm_radumtp: Insertion failed in nas port walk."); return 1; } return 0;}/* * Walk through the offset tree, operating on the cache */static int offset_walk(void *context, void *data){ offset_walk_t *walk = context; NAS_PORT *nas_port = data; struct radutmp utmp; radutmp_simul_t *user, myUser; /* * Seek to the entry, and possibly re-write it. */ if (lseek(walk->fd, nas_port->offset, SEEK_SET) < 0) { rad_assert(0 == 1); } if (read(walk->fd, &utmp, sizeof(utmp)) != sizeof(utmp)) { rad_assert(0 == 1); } /* * If the entry in the file is NEWER than the reboot * packet, don't re-write it, and don't delete it. */ if (utmp.time > walk->now) { return 0; } utmp.type = P_IDLE; utmp.time = walk->now; if (lseek(walk->fd, -(off_t)sizeof(utmp), SEEK_CUR) < 0) { radlog(L_ERR, "rlm_radutmp: offset_walk: failed in lseek: %s", strerror(errno)); return 1; } write(walk->fd, &utmp, sizeof(utmp)); strlcpy(myUser.login, utmp.login, sizeof(myUser.login)); user = rbtree_finddata(walk->inst->user_tree, &myUser); rad_assert(user != NULL); rad_assert(user->simul_count > 0); user->simul_count--; if (user->simul_count == 0) { rbtree_deletebydata(walk->inst->user_tree, user); } if (rbtree_deletebydata(walk->cache->nas_ports, nas_port) == 0) { radlog(L_ERR, "rlm_radutmp: Failed to delete entry from cache"); return 1; } /* * Insert the entry into the free list. */ nas_port->next = walk->cache->free_offsets; walk->cache->free_offsets = nas_port; return 0;}/* * Zap all users on a NAS from the radutmp file. */static int radutmp_zap(rlm_radutmp_t *inst, radutmp_cache_t *cache, uint32_t nas_address, time_t now){ int rcode; rbtree_t *offset_tree; offset_walk_t walk; rad_assert(now != 0); /* * If there's nothing in the file, do nothing, * but truncate the file, just to be safe. */ if (rbtree_num_elements(cache->nas_ports) == 0) { truncate(cache->filename, (off_t) 0); DEBUG2(" rlm_radutmp: No entries in file. Quenching zap."); return 1; } /* * Create the offset tree, as we want to delete utmp * entries starting from the start of the file, and we * can't delete nodes from an rbtree while we're walking * it. */ offset_tree = rbtree_create(offset_cmp, NULL, 0); if (!offset_tree) { radlog(L_ERR, "rlm_radutmp: Out of memory"); return 0; } pthread_mutex_lock(&cache->mutex); /* * Walk through the cache, finding entries for this NAS, * and add those entries to the offset tree. */ memset(&walk, 0, sizeof(walk)); walk.inst = inst; walk.offset_tree = offset_tree; walk.nas_address = nas_address; rcode = rbtree_walk(cache->nas_ports, PreOrder, nas_port_walk, &walk); if (rcode != 0) { pthread_mutex_unlock(&cache->mutex); rbtree_free(offset_tree); radlog(L_ERR, "rlm_radutmp: Failed walking the cache."); return 0; } /* * If both trees have the same number of elements, then * don't do anything special, as UDP packets may be * received out of order, by several seconds. The * "offset_walk" routine MAY NOT delete the entries, if * it sees that the entries in the file are newer than * the reboot packet. */ /* * If there's nothing to do, don't do anything. */ if (rbtree_num_elements(offset_tree) == 0) { DEBUG2(" rlm_radutmp: NAS IP %08x has no users recorded in file %s.", htonl(nas_address), cache->filename); pthread_mutex_unlock(&cache->mutex); rbtree_free(offset_tree); return 1; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -