#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";
typedef struct nas_port {
uint32_t nas_address;
unsigned int nas_port;
off_t offset;
struct nas_port *next;
} NAS_PORT;
typedef struct radutmp_cache_t {
const char *filename;
time_t last_used;
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;
typedef struct radutmp_simul_t {
char login[sizeof(((struct radutmp *) NULL)->login) + 1];
int simul_count;
} radutmp_simul_t;
typedef struct rlm_radutmp_t {
char *filename;
char *username;
int case_sensitive;
int check_nas;
int permission;
int callerid_ok;
rbtree_t *user_tree;
radutmp_cache_t cache;
} rlm_radutmp_t;
#ifndef HAVE_PTHREAD_H
#define pthread_mutex_init(_x, _y)
#define pthread_mutex_destroy(_x)
#define pthread_mutex_lock(_x)
#define pthread_mutex_unlock(_x)
#endif
static 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 }
};
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;
}
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);
}
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);
}
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;
}
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;
}
static int cache_reset(rlm_radutmp_t *inst, radutmp_cache_t *cache)
{
NAS_PORT *this, *next;
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;
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;
}
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;
}
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;
static int nas_port_walk(void *context, void *data)
{
offset_walk_t *walk = context;
NAS_PORT *nas_port = data;
if (walk->nas_address != nas_port->nas_address) return 0;
if (rbtree_insert(walk->offset_tree, nas_port) != 1) {
DEBUG2(" rlm_radumtp: Insertion failed in nas port walk.");
return 1;
}
return 0;
}
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;
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 (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;
}
nas_port->next = walk->cache->free_offsets;
walk->cache->free_offsets = nas_port;
return 0;
}
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 (rbtree_num_elements(cache->nas_ports) == 0) {
truncate(cache->filename, (off_t) 0);
DEBUG2(" rlm_radutmp: No entries in file. Quenching zap.");
return 1;
}
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);
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 (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;
}
walk.fd = open(cache->filename, O_RDWR);
if (walk.fd < 0) {
pthread_mutex_unlock(&cache->mutex);
rbtree_free(offset_tree);
radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
cache->filename, strerror(errno));
return 0;
}
rad_lockfd(walk.fd, LOCK_LEN);
walk.cache = cache;
walk.now = now;
rcode = rbtree_walk(offset_tree, InOrder, offset_walk, &walk);
rbtree_free(offset_tree);
if (rcode != 0) {
radlog(L_ERR, "rlm_radutmp: Failed walking the offsets.");
return 0;
}
close(walk.fd);
if (rbtree_num_elements(cache->nas_ports) == 0) {
NAS_PORT *this, *next;
for (this = inst->cache.free_offsets;
this != NULL;
this = next) {
next = this->next;
free(this);
}
truncate(cache->filename, 0);
rad_assert(rbtree_num_elements(inst->user_tree) == 0);
}
pthread_mutex_unlock(&cache->mutex);
return 1;
}
static int cache_file(rlm_radutmp_t *inst, radutmp_cache_t *cache)
{
int fd;
int read_size;
struct stat buf;
struct radutmp utmp;
NAS_PORT **tail;
rad_assert(cache->max_offset == 0);
rad_assert(cache->free_offsets == NULL);
if (stat(cache->filename, &buf) < 0) {
if (errno == ENOENT) {
cache->cached_file = 1;
return 0;
}
radlog(L_ERR, "rlm_radutmp: Cannot stat %s: %s",
cache->filename, strerror(errno));
return 1;
}
if (buf.st_size == 0) {
cache->cached_file = 1;
return 0;
}
pthread_mutex_lock(&cache->mutex);
fd = open(cache->filename, O_RDONLY, cache->permission);
if (fd < 0) {
pthread_mutex_unlock(&cache->mutex);
radlog(L_ERR, "rlm_radutmp: Error opening %s: %s",
cache->filename, strerror(errno));
return 1;
}
tail = &(cache->free_offsets);
do {
read_size = read(fd, &utmp, sizeof(utmp));
if (read_size == sizeof(utmp)) {
radutmp_simul_t *user, myUser;
NAS_PORT *nas_port = rad_malloc(sizeof(*nas_port));
memset(nas_port, 0, sizeof(nas_port));
nas_port->offset = cache->max_offset;
cache->max_offset += sizeof(utmp);
if (utmp.type == P_IDLE) {
*tail = nas_port;
tail = &(nas_port->next);
continue;
}
nas_port->nas_address = utmp.nas_address;
nas_port->nas_port = utmp.nas_port;
if (!rbtree_insert(cache->nas_ports, nas_port)) {
rad_assert(0 == 1);
}
strlcpy(myUser.login, utmp.login, sizeof(myUser.login));
user = rbtree_finddata(inst->user_tree, &myUser);
if (user) {
user->simul_count++;
} else {
user = rad_malloc(sizeof(user));
strlcpy(user->login, utmp.login,
sizeof(user->login));
user->simul_count = 1;
if (!rbtree_insert(inst->user_tree, user)) {
rad_assert(0 == 1);
}
}
continue;
}
if (read_size != 0) {
pthread_mutex_unlock(&cache->mutex);
close(fd);
radlog(L_ERR, "rlm_radutmp: Badly formed file %s",
cache->filename);
return 1;
}
} while (read_size != 0);
pthread_mutex_unlock(&cache->mutex);
close(fd);
cache->cached_file = 1;
return 0;
}
static int radutmp_accounting(void *instance, REQUEST *request)
{
rlm_radutmp_t *inst = instance;
struct radutmp utmp, u;
VALUE_PAIR *vp;
int status = -1;
uint32_t nas_address = 0;
uint32_t framed_address = 0;
int protocol = -1;
int fd;
int port_seen = 0;
char buffer[256];
char filename[1024];
char ip_name[32];
const char *nas;
NAS_PORT *nas_port, myPort;
radutmp_cache_t *cache;
int read_size;
rbnode_t *node;
if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) == NULL) {
radlog(L_ERR, "rlm_radutmp: No Accounting-Status-Type record.");
return RLM_MODULE_NOOP;
}
status = vp->vp_integer;
if ((status != PW_STATUS_ACCOUNTING_ON) &&
(status != PW_STATUS_ACCOUNTING_OFF)) do {
int check1 = 0;
int check2 = 0;
if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME))
== NULL || vp->vp_date == 0)
check1 = 1;
if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID))
!= NULL && vp->length == 8 &&
memcmp(vp->vp_strvalue, "00000000", 8) == 0)
check2 = 1;
if (check1 == 0 || check2 == 0) {
#if 0
radlog(L_ERR, "rlm_radutmp: no username in record");
return RLM_MODULE_FAIL;
#else
break;
#endif
}
radlog(L_INFO, "rlm_radutmp: converting reboot records.");
if (status == PW_STATUS_STOP)
status = PW_STATUS_ACCOUNTING_OFF;
if (status == PW_STATUS_START)
status = PW_STATUS_ACCOUNTING_ON;
} while(0);
memset(&utmp, 0, sizeof(utmp));
utmp.porttype = 'A';
for (vp = request->packet->vps; vp; vp = vp->next) {
switch (vp->attribute) {
case PW_LOGIN_IP_HOST:
case PW_FRAMED_IP_ADDRESS:
framed_address = vp->vp_ipaddr;
utmp.framed_address = vp->vp_ipaddr;
break;
case PW_FRAMED_PROTOCOL:
protocol = vp->vp_integer;
break;
case PW_NAS_IP_ADDRESS:
nas_address = vp->vp_ipaddr;
utmp.nas_address = vp->vp_ipaddr;
break;
case PW_NAS_PORT:
utmp.nas_port = vp->vp_integer;
port_seen = 1;
break;
case PW_ACCT_DELAY_TIME:
utmp.delay = vp->vp_integer;
break;
case PW_ACCT_SESSION_ID:
if (vp->length > sizeof(utmp.session_id)) {
int length = vp->length - sizeof(utmp.session_id);
if (vp->vp_strvalue[vp->length - 1] == 0) {
length--;
}
memcpy(utmp.session_id,
vp->vp_strvalue + length,
sizeof(utmp.session_id));
} else {
memset(utmp.session_id, 0,
sizeof(utmp.session_id));
memcpy(utmp.session_id,
vp->vp_strvalue,
vp->length);
}
break;
case PW_NAS_PORT_TYPE:
if (vp->vp_integer <= 4)
utmp.porttype = porttypes[vp->vp_integer];
break;
case PW_CALLING_STATION_ID:
if(inst->callerid_ok)
strlcpy(utmp.caller_id,
(char *)vp->vp_strvalue,
sizeof(utmp.caller_id));
break;
}
}
if (nas_address == 0) {
nas_address = request->packet->src_ipaddr;
utmp.nas_address = nas_address;
nas = request->client->shortname;
} else if (request->packet->src_ipaddr.ipaddr.ip4addr.s_addr == nas_address) {
nas = request->client->shortname;
} else {
nas = ip_ntoa(ip_name, nas_address);
}
if (protocol == PW_PPP)
utmp.proto = 'P';
else if (protocol == PW_SLIP)
utmp.proto = 'S';
else
utmp.proto = 'T';
utmp.time = request->timestamp - utmp.delay;
radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);
cache = &inst->cache;
if (!cache->filename) {
cache->filename = strdup(filename);
rad_assert(cache->filename != NULL);
} else if (strcmp(cache->filename, filename) != 0) {
radlog(L_ERR, "rlm_radutmp: We do not support dynamically named files.");
return RLM_MODULE_FAIL;
}
cache->last_used = request->timestamp;
if (!cache->cached_file) {
cache_file(inst, cache);
}
if (status == PW_STATUS_ACCOUNTING_ON && nas_address) {
radlog(L_INFO, "rlm_radutmp: NAS %s restarted (Accounting-On packet seen)",
nas);
if (!radutmp_zap(inst, cache, nas_address, utmp.time)) {
rad_assert(0 == 1);
}
return RLM_MODULE_OK;
}
if (status == PW_STATUS_ACCOUNTING_OFF && nas_address) {
radlog(L_INFO, "rlm_radutmp: NAS %s rebooted (Accounting-Off packet seen)",
nas);
if (!radutmp_zap(inst, cache, nas_address, utmp.time)) {
rad_assert(0 == 1);
}
return RLM_MODULE_OK;
}
if (status != PW_STATUS_START &&
status != PW_STATUS_STOP &&
status != PW_STATUS_ALIVE) {
radlog(L_ERR, "rlm_radutmp: NAS %s port %u unknown packet type %d, ignoring it.",
nas, utmp.nas_port, status);
return RLM_MODULE_NOOP;
}
if (!port_seen) {
DEBUG2(" rlm_radutmp: No NAS-Port in the packet. Cannot do anything.");
DEBUG2(" rlm_radumtp: WARNING: checkrad will probably not work!");
return RLM_MODULE_NOOP;
}
*buffer = '\0';
radius_xlat(buffer, sizeof(buffer), inst->username, request, NULL);
if (strcmp(buffer, "!root") == 0) {
DEBUG2(" rlm_radutmp: Not recording administrative user");
return RLM_MODULE_NOOP;
}
strlcpy(utmp.login, buffer, RUT_NAMESIZE);
fd = open(cache->filename, O_RDWR, inst->permission);
if (fd < 0) {
if (errno == ENOENT) {
DEBUG2(" rlm_radutmp: File %s doesn't exist, creating it.", cache->filename);
if (!cache_reset(inst, cache)) return RLM_MODULE_FAIL;
fd = open(cache->filename, O_RDWR | O_CREAT,
inst->permission);
}
} else {
struct stat buf;
if ((stat(cache->filename, &buf) == 0) &&
(buf.st_size == 0) &&
(!cache_reset(inst, cache))) {
return RLM_MODULE_FAIL;
}
DEBUG2(" rlm_radutmp: File %s was truncated. Resetting cache.",
cache->filename);
}
if (fd < 0) {
radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
cache->filename, strerror(errno));
return RLM_MODULE_FAIL;
}
myPort.nas_address = utmp.nas_address;
myPort.nas_port = utmp.nas_port;
pthread_mutex_lock(&cache->mutex);
node = rbtree_find(cache->nas_ports, &myPort);
pthread_mutex_unlock(&cache->mutex);
if (node) {
nas_port = rbtree_node2data(cache->nas_ports, node);
#if 0
pthread_mutex_lock(&cache->mutex);
node = rbtree_find(cache->nas_ports, &myPort);
pthread_mutex_unlock(&cache->mutex);
#endif
}
if (!node) {
radutmp_simul_t *user;
if (status == PW_STATUS_STOP) {
DEBUG2(" rlm_radumtp: Logout entry for NAS %s port %u with no Login: ignoring it.",
nas, utmp.nas_port);
return RLM_MODULE_NOOP;
}
pthread_mutex_lock(&cache->mutex);
if (!cache->free_offsets) {
cache->free_offsets = rad_malloc(sizeof(NAS_PORT));
memset(cache->free_offsets, 0,
sizeof(*(cache->free_offsets)));
cache->free_offsets->offset = cache->max_offset;
cache->max_offset += sizeof(u);
}
nas_port = cache->free_offsets;
cache->free_offsets = nas_port->next;
nas_port->nas_address = nas_address;
nas_port->nas_port = utmp.nas_port;
if (!rbtree_insert(cache->nas_ports, nas_port)) {
rad_assert(0 == 1);
}
user = rad_malloc(sizeof(user));
strlcpy(user->login, utmp.login,
sizeof(user->login));
user->simul_count = 1;
if (!rbtree_insert(inst->user_tree, user)) {
rad_assert(0 == 1);
}
pthread_mutex_unlock(&cache->mutex);
}
lseek(fd, nas_port->offset, SEEK_SET);
rad_lockfd(fd, LOCK_LEN);
if (node) {
read_size = read(fd, &u, sizeof(u));
if ((read_size < 0) ||
((read_size > 0) && (read_size != sizeof(u)))) {
radlog(L_ERR, "rlm_radutmp: Badly formed file %s",
cache->filename);
close(fd);
return RLM_MODULE_FAIL;
}
rad_assert(read_size != 0);
if (read_size > 0) {
rad_assert(u.type == P_LOGIN);
rad_assert(u.nas_address == utmp.nas_address);
rad_assert(u.nas_port == utmp.nas_port);
if (strncmp(utmp.session_id, u.session_id,
sizeof(u.session_id)) == 0) {
if (status == PW_STATUS_START) {
DEBUG2(" rlm_radutmp: Login entry for NAS %s port %u duplicate, ignoring it.",
nas, u.nas_port);
close(fd);
return RLM_MODULE_OK;
} else if (status == PW_STATUS_ALIVE) {
utmp.time = u.time;
} else if (status == PW_STATUS_STOP) {
radutmp_simul_t *user, myUser;
pthread_mutex_lock(&cache->mutex);
rbtree_deletebydata(cache->nas_ports,
nas_port);
strlcpy(myUser.login,
u.login, sizeof(myUser.login));
user = rbtree_finddata(inst->user_tree,
&myUser);
rad_assert(user != NULL);
rad_assert(user->simul_count > 0);
user->simul_count--;
if (user->simul_count == 0) {
rbtree_deletebydata(inst->user_tree, user);
}
pthread_mutex_unlock(&cache->mutex);
} else {
rad_assert(0 == 1);
}
} else {
if (status == PW_STATUS_STOP) {
radlog(L_ERR, "rlm_radutmp: Logout entry for NAS %s port %u has old Acct-Session-ID, ignoring it.",
nas, u.nas_port);
close(fd);
return RLM_MODULE_OK;
}
}
}
}
lseek(fd, nas_port->offset, SEEK_SET);
if (status != PW_STATUS_STOP) {
utmp.type = P_LOGIN;
rad_assert(nas_port != NULL);
} else {
memcpy(&utmp, &u, sizeof(utmp));
utmp.type = P_IDLE;
}
write(fd, &utmp, sizeof(utmp));
close(fd);
return RLM_MODULE_OK;
}
static int radutmp_checksimul(void *instance, REQUEST *request)
{
struct radutmp u;
int fd;
VALUE_PAIR *vp;
uint32_t ipno = 0;
char *call_num = NULL;
int rcode;
rlm_radutmp_t *inst = instance;
char login[256];
char filename[1024];
radutmp_cache_t *cache;
radutmp_simul_t *user, myUser;
radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);
cache = &inst->cache;
if (!cache->filename) {
cache->filename = strdup(filename);
rad_assert(cache->filename != NULL);
} else if (strcmp(cache->filename, filename) != 0) {
radlog(L_ERR, "rlm_radutmp: We do not support dynamically named files.");
return RLM_MODULE_FAIL;
}
*login = '\0';
radius_xlat(login, sizeof(login), inst->username, request, NULL);
if (!*login) {
return RLM_MODULE_NOOP;
}
request->simul_count = 0;
strlcpy(myUser.login, login, sizeof(myUser.login));
pthread_mutex_lock(&inst->cache.mutex);
user = rbtree_finddata(inst->user_tree, &myUser);
if (user) request->simul_count = user->simul_count;
user = NULL;
pthread_mutex_unlock(&inst->cache.mutex);
if ((request->simul_count < request->simul_max) ||
!inst->check_nas) {
return RLM_MODULE_OK;
}
if ((fd = open(cache->filename, O_RDWR)) < 0) {
if (errno == ENOENT) {
request->simul_count = 0;
return RLM_MODULE_OK;
}
radlog(L_ERR, "rlm_radumtp: Error accessing file %s: %s",
cache->filename, strerror(errno));
return RLM_MODULE_FAIL;
}
if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
ipno = vp->vp_ipaddr;
if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
call_num = vp->vp_strvalue;
rad_lockfd(fd, LOCK_LEN);
request->simul_count = 0;
while (read(fd, &u, sizeof(u)) == sizeof(u)) {
if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
(!inst->case_sensitive &&
(strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
(u.type == P_LOGIN)) {
char session_id[sizeof(u.session_id) + 1];
char utmp_login[sizeof(u.login) + 1];
strlcpy(session_id, u.session_id, sizeof(session_id));
strlcpy(utmp_login, u.login, sizeof(u.login));
rad_unlockfd(fd, LOCK_LEN);
rcode = rad_check_ts(u.nas_address, u.nas_port,
utmp_login, session_id);
rad_lockfd(fd, LOCK_LEN);
if (rcode == 0) {
session_zap(request, u.nas_address,
u.nas_port, login, session_id,
u.framed_address, u.proto,0);
}
else if (rcode == 1) {
++request->simul_count;
if (strchr("SCPA", u.proto) &&
ipno && u.framed_address == ipno)
request->simul_mpp = 2;
else if (strchr("SCPA", u.proto) && call_num &&
!strncmp(u.caller_id,call_num,16))
request->simul_mpp = 2;
}
else {
close(fd);
radlog(L_ERR, "rlm_radutmp: Failed to check the terminal server for user '%s'.", utmp_login);
return RLM_MODULE_FAIL;
}
}
}
close(fd);
return RLM_MODULE_OK;
}
module_t rlm_radutmp = {
"radutmp",
0,
NULL,
radutmp_instantiate,
{
NULL,
NULL,
NULL,
radutmp_accounting,
radutmp_checksimul,
NULL,
NULL,
NULL
},
radutmp_detach,
NULL,
};