📄 rlm_perl.c
字号:
/* * rlm_perl.c * * Version: $Id: rlm_perl.c,v 1.13 2004/02/26 19:04:34 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 2002 The FreeRADIUS server project * Copyright 2002 Boian Jordanov <bjordanov@orbitel.bg> */#include "autoconf.h"#include "libradius.h"#include <stdio.h>#include <stdlib.h>#include <string.h>#include "radiusd.h"#include "modules.h"#include "conffile.h"#ifdef DEBUG#undef DEBUG#endif#ifdef INADDR_ANY#undef INADDR_ANY#endif#ifdef INADDR_NONE#undef INADDR_NONE#endif#include <EXTERN.h>#include <perl.h>#include <XSUB.h>#include <dlfcn.h>#include <semaphore.h>static const char rcsid[] = "$Id: rlm_perl.c,v 1.13 2004/02/26 19:04:34 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 perl_inst { /* Name of the perl module */ char *module; /* Name of the functions for each module method */ char *func_authorize; char *func_authenticate; char *func_accounting; char *func_start_accounting; char *func_stop_accounting; char *func_preacct; char *func_checksimul; char *func_detach; char *func_xlat; char *xlat_name; char *perl_flags;} PERL_INST;/* * 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[] = { { "module", PW_TYPE_STRING_PTR, offsetof(PERL_INST,module), NULL, "module"}, { "func_authorize", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_authorize), NULL, "authorize"}, { "func_authenticate", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_authenticate), NULL, "authenticate"}, { "func_accounting", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_accounting), NULL, "accounting"}, { "func_preacct", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_preacct), NULL, "preacct"}, { "func_checksimul", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_checksimul), NULL, "checksimul"}, { "func_detach", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_detach), NULL, "detach"}, { "func_xlat", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_xlat), NULL, "xlat"}, { "perl_flags", PW_TYPE_STRING_PTR, offsetof(PERL_INST,perl_flags), NULL, NULL}, { "func_start_accounting", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_start_accounting), NULL, NULL}, { "func_stop_accounting", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_stop_accounting), NULL, NULL}, { NULL, -1, 0, NULL, NULL } /* end the list */};/* * man perlembed */EXTERN_C void boot_DynaLoader(pTHX_ CV* cv);/* * We share one perl interpreter among all of the instances * of this module. And clone it for every thread if we have perl * with -Duseithreads compiled in */static PerlInterpreter *interp;#ifdef USE_ITHREADS/* * Pool of Perl's clones (genetically cloned) ;) * */typedef struct pool_handle { struct pool_handle *next; struct pool_handle *prev; enum {busy, idle} status; unsigned int request_count; PerlInterpreter *clone;} POOL_HANDLE;typedef struct PERL_POOL { POOL_HANDLE *head; POOL_HANDLE *tail; int current_clones; int active_clones; int max_clones; int start_clones; int min_spare_clones; int max_spare_clones; int max_request_per_clone; int cleanup_delay; perl_mutex mutex; time_t time_when_last_added;} PERL_POOL;static PERL_POOL perl_pool;static const CONF_PARSER pool_conf[] = { { "max_clones", PW_TYPE_INTEGER, 0, &perl_pool.max_clones, "32"}, { "start_clones",PW_TYPE_INTEGER, 0, &perl_pool.start_clones, "5"}, { "min_spare_clones",PW_TYPE_INTEGER, 0, &perl_pool.min_spare_clones, "3"}, { "max_spare_clones",PW_TYPE_INTEGER, 0, &perl_pool.max_spare_clones, "3"}, { "cleanup_delay",PW_TYPE_INTEGER, 0, &perl_pool.cleanup_delay, "5"}, { "max_request_per_clone",PW_TYPE_INTEGER, 0, &perl_pool.max_request_per_clone, "0"}, { NULL, -1, 0, NULL, NULL } /* end the list */};#define dl_librefs "DynaLoader::dl_librefs"#define dl_modules "DynaLoader::dl_modules"static void rlm_perl_clear_handles(pTHX){ AV *librefs = get_av(dl_librefs, FALSE); if (librefs) { av_clear(librefs); }}static void **rlm_perl_get_handles(pTHX){ I32 i; AV *librefs = get_av(dl_librefs, FALSE); AV *modules = get_av(dl_modules, FALSE); void **handles; if (!librefs) { radlog(L_ERR, "Could not get @%s for unloading.\n", dl_librefs); return NULL; } if (!(AvFILL(librefs) >= 0)) { return NULL; } handles = (void **)rad_malloc(sizeof(void *) * (AvFILL(librefs)+2)); for (i=0; i<=AvFILL(librefs); i++) { void *handle; SV *handle_sv = *av_fetch(librefs, i, FALSE); if(!handle_sv) { radlog(L_ERR, "Could not fetch $%s[%d]!\n", dl_librefs, (int)i); continue; } handle = (void *)SvIV(handle_sv); if (handle) { handles[i] = handle; } } av_clear(modules); av_clear(librefs); handles[i] = (void *)0; return handles;}static void rlm_perl_close_handles(void **handles){ int i; if (!handles) { return; } for (i=0; handles[i]; i++) { radlog(L_DBG, "close 0x%lx\n", (unsigned long)handles[i]); dlclose(handles[i]); } free(handles);}static PerlInterpreter *rlm_perl_clone(){ PerlInterpreter *clone; UV clone_flags = CLONEf_KEEP_PTR_TABLE; PERL_SET_CONTEXT(interp); clone = perl_clone(interp, clone_flags); { dTHXa(clone); } ptr_table_free(PL_ptr_table); PL_ptr_table = NULL; PERL_SET_CONTEXT(aTHX); rlm_perl_clear_handles(aTHX); return clone;}static void rlm_perl_destruct(PerlInterpreter *perl){ char **orig_environ = NULL; dTHXa(perl); PERL_SET_CONTEXT(perl); PL_perl_destruct_level = 2; PL_origenviron = environ; { dTHXa(perl); } /* * FIXME: This shouldn't happen * */ while (PL_scopestack_ix > 1 ){ LEAVE; } perl_destruct(perl); perl_free(perl); if (orig_environ) { environ = orig_environ; }}static void rlm_destroy_perl(PerlInterpreter *perl){ void **handles; dTHXa(perl); PERL_SET_CONTEXT(perl); handles = rlm_perl_get_handles(aTHX); rlm_perl_destruct(perl); rlm_perl_close_handles(handles);}static void delete_pool_handle(POOL_HANDLE *handle){ POOL_HANDLE *prev; POOL_HANDLE *next; prev = handle->prev; next = handle->next; if (prev == NULL) { perl_pool.head = next; } else { prev->next = next; } if (next == NULL) { perl_pool.tail = prev; } else { next->prev = prev; } perl_pool.current_clones--;}static void move2tail(POOL_HANDLE *handle){ POOL_HANDLE *prev; POOL_HANDLE *next; if (perl_pool.head == NULL) { handle->prev = NULL; handle->next = NULL; perl_pool.head = handle; perl_pool.tail = handle; return; } if (perl_pool.tail == handle) { return; } prev = handle->prev; next = handle->next; if ((next != NULL) || (prev != NULL)) { if (next == NULL) { return; } if (prev == NULL) { perl_pool.head = next; next->prev = NULL; } else { prev->next = next; next->prev = prev; } } handle->next = NULL; prev = perl_pool.tail; perl_pool.tail = handle; handle->prev = prev; prev->next = handle;}static POOL_HANDLE *pool_grow () { POOL_HANDLE *handle; time_t now; if (perl_pool.max_clones == perl_pool.current_clones) { return NULL; } handle = (POOL_HANDLE *)rad_malloc(sizeof(POOL_HANDLE)); if (!handle) { radlog(L_ERR,"Could not find free memory for pool. Aborting"); return NULL; } handle->prev = NULL; handle->next = NULL; handle->status = idle; handle->clone = rlm_perl_clone(); handle->request_count = 0; perl_pool.current_clones++; move2tail(handle); now = time(NULL); perl_pool.time_when_last_added = now; return handle;}static POOL_HANDLE *pool_pop(){ POOL_HANDLE *handle; POOL_HANDLE *found; POOL_HANDLE *tmp; /* * Lock the pool and be fast other thread maybe * waiting for us to finish */ MUTEX_LOCK(&perl_pool.mutex); found = NULL; for (handle = perl_pool.head; handle ; handle = tmp) { tmp = handle->next; if (handle->status == idle){ found = handle; break; } } if (found == NULL) { if (perl_pool.current_clones < perl_pool.max_clones ) { found = pool_grow(); perl_pool.current_clones++; if (found == NULL) { radlog(L_ERR,"Cannot grow pool returning"); MUTEX_UNLOCK(&perl_pool.mutex); return NULL; } } else { radlog(L_ERR,"reached maximum clones %d cannot grow", perl_pool.current_clones); MUTEX_UNLOCK(&perl_pool.mutex); return NULL; } } move2tail(found); found->status = busy; perl_pool.active_clones++; found->request_count++; /* * Hurry Up */ MUTEX_UNLOCK(&perl_pool.mutex); radlog(L_DBG,"perl_pool: item 0x%lx asigned new request. Handled so far: %d", (unsigned long) found->clone, found->request_count); return found;}static int pool_release(POOL_HANDLE *handle) { POOL_HANDLE *tmp, *tmp2; int spare, i, t; time_t now; /* * Lock it */ MUTEX_LOCK(&perl_pool.mutex); handle->status = idle; perl_pool.active_clones--; spare = perl_pool.current_clones - perl_pool.active_clones; radlog(L_DBG,"perl_pool total/active/spare [%d/%d/%d]" , perl_pool.current_clones, perl_pool.active_clones, spare); if (spare < perl_pool.min_spare_clones) { t = perl_pool.min_spare_clones - spare; for (i=0;i<t; i++) { if ((tmp = pool_grow()) == NULL) { MUTEX_UNLOCK(&perl_pool.mutex); return -1; } } MUTEX_UNLOCK(&perl_pool.mutex); return 0; } now = time(NULL); if ((now - perl_pool.time_when_last_added) < perl_pool.cleanup_delay) { MUTEX_UNLOCK(&perl_pool.mutex); return 0; } if (spare > perl_pool.max_spare_clones) { spare -= perl_pool.max_spare_clones; for (tmp = perl_pool.head; (tmp !=NULL ) && (spare > 0) ; tmp = tmp2) { tmp2 = tmp->next; if(tmp->status == idle) { rlm_destroy_perl(tmp->clone); delete_pool_handle(tmp); perl_pool.current_clones--; spare--; break; } } } /* * Hurry Up :) */ MUTEX_UNLOCK(&perl_pool.mutex); return 0;}static int init_pool (CONF_SECTION *conf) { POOL_HANDLE *handle; int t; MUTEX_INIT(&perl_pool.mutex); /* * Read The Config * */ cf_section_parse(conf,NULL,pool_conf); for(t = 0;t < perl_pool.start_clones ;t++){ if ((handle = pool_grow()) == NULL) { return -1; } } return 1;}#endif/* * Do any per-module initialization. e.g. set up connections * to external databases, read configuration files, set up * dictionary entries, etc. * * Try to avoid putting too much stuff in here - it's better to * do it in instantiate() where it is not global. */static int perl_init(void){ if ((interp = perl_alloc()) == NULL) { radlog(L_INFO, "rlm_perl: No memory for allocating new perl !"); return -1; } perl_construct(interp); PL_perl_destruct_level = 2; return 0;}static void xs_init(pTHX){ char *file = __FILE__; /* DynaLoader is a special case */ newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);}/* * * This is wrapper for radlog * Now users can call radiusd::radlog(level,msg) wich is the same * calling radlog from C code. * Boyan */static XS(XS_radiusd_radlog){ dXSARGS; if (items !=2) croak("Usage: radiusd::radlog(level, message)"); { int level; char *msg; level = (int) SvIV(ST(0)); msg = (char *) SvPV(ST(1), PL_na); /* * Because 'msg' is a 'char *', we don't want '%s', etc. * in it to give us printf-style vulnerabilities. */ radlog(level, "rlm_perl: %s", msg); } XSRETURN_NO;}/* * The xlat function */static int perl_xlat(void *instance, REQUEST *request, char *fmt, char * out, int freespace, RADIUS_ESCAPE_STRING func){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -