📄 rlm_perl.c
字号:
PERL_INST *inst= (PERL_INST *) instance; PerlInterpreter *perl; char params[1024], *tmp_ptr, *ptr, *tmp; int count, ret; STRLEN n_a; perl = interp;#ifdef USE_ITHREADS POOL_HANDLE *handle; if ((handle = pool_pop()) == NULL) { return 0; } perl = handle->clone; radlog(L_DBG,"Found a interpetator 0x%lx",(unsigned long) perl); { dTHXa(perl); }#endif { dSP; ENTER;SAVETMPS; /* * Do an xlat on the provided string (nice recursive operation). */ if (!radius_xlat(params, sizeof(params), fmt, request, func)) { radlog(L_ERR, "rlm_perl: xlat failed."); return 0; } ptr = strtok(params, " "); PUSHMARK(SP); XPUSHs(sv_2mortal(newSVpv(ptr,0))); while ((tmp_ptr = strtok(NULL, " ")) != NULL) { XPUSHs(sv_2mortal(newSVpv(tmp_ptr,0))); } PUTBACK; count = call_pv(inst->func_xlat, G_SCALAR | G_EVAL); SPAGAIN; if (SvTRUE(ERRSV)) { radlog(L_ERR, "rlm_perl: perl_xlat exit %s\n", SvPV(ERRSV,n_a)); return 0; } if (count > 0) { tmp = POPp; ret = strlen(tmp); strncpy(out,tmp,ret); radlog(L_DBG,"rlm_perl: Len is %d , out is %s freespace is %d", ret, out,freespace); PUTBACK ; FREETMPS ; LEAVE ; if (ret <= freespace) return ret; } }#ifdef USE_ITHREADS pool_release(handle);#endif return 0;}/* * 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. * * Boyan: * Setup a hashes wich we will use later * parse a module and give him a chance to live * */static int perl_instantiate(CONF_SECTION *conf, void **instance){ PERL_INST *inst = (PERL_INST *) instance; HV *rad_reply_hv = newHV(); HV *rad_check_hv = newHV(); HV *rad_request_hv = newHV(); char *embed[4], *xlat_name; int exitstatus = 0, argc=0; /* * Set up a storage area for instance data */ inst = rad_malloc(sizeof(PERL_INST)); memset(inst, 0, sizeof(PERL_INST)); /* * If the configuration parameters can't be parsed, then * fail. */ if (cf_section_parse(conf, inst, module_config) < 0) { free(inst); return -1; } embed[0] = NULL; if (inst->perl_flags) { embed[1] = inst->perl_flags; embed[2] = inst->module; embed[3] = "0"; argc = 4; } else { embed[1] = inst->module; embed[2] = "0"; argc = 3; } exitstatus = perl_parse(interp, xs_init, argc, embed, NULL);#if PERL_REVISION >= 5 && PERL_VERSION >=8 PL_exit_flags |= PERL_EXIT_DESTRUCT_END;#endif if(!exitstatus) { exitstatus = perl_run(interp); } else { radlog(L_INFO,"rlm_perl: perl_parse failed: %s not found or has syntax errors. \n", inst->module); return (-1); } newXS("radiusd::radlog",XS_radiusd_radlog, "rlm_perl.c"); rad_reply_hv = get_hv("RAD_REPLY",1); rad_check_hv = get_hv("RAD_CHECK",1); rad_request_hv = get_hv("RAD_REQUEST",1); xlat_name = cf_section_name2(conf); if (xlat_name == NULL) xlat_name = cf_section_name1(conf); if (xlat_name){ inst->xlat_name = strdup(xlat_name); xlat_register(xlat_name, perl_xlat, inst); }#ifdef USE_ITHREADS if ((init_pool(conf)) == -1) { radlog(L_ERR,"Couldn't init a pool of perl clones. Exiting"); return -1; }#endif *instance = inst; return 0;}/* * get the vps and put them in perl hash * If one VP have multiple values it is added as array_ref * Example for this is Cisco-AVPair that holds multiple values. * Which will be available as array_ref in $RAD_REQUEST{'Cisco-AVPair'} */static void perl_store_vps(VALUE_PAIR *vp, HV *rad_hv){ VALUE_PAIR *nvp, *vpa, *vpn; AV *av; char buffer[1024]; int attr, len; hv_clear(rad_hv); nvp = paircopy(vp); while (nvp != NULL) { attr = nvp->attribute; vpa = paircopy2(nvp,attr); if (vpa->next) { av = newAV(); vpn = vpa; while (vpn) { len = vp_prints_value(buffer, sizeof(buffer), vpn, FALSE); av_push(av, newSVpv(buffer, len)); vpn = vpn->next; } hv_store(rad_hv, nvp->name, strlen(nvp->name), newRV((SV *) av), 0); } else { len = vp_prints_value(buffer, sizeof(buffer), vpa, FALSE); hv_store(rad_hv, vpa->name, strlen(vpa->name), newSVpv(buffer, len), 0); } pairfree(&vpa); vpa = nvp; while ((vpa != NULL) && (vpa->attribute == attr)) vpa = vpa->next; pairdelete(&nvp, attr); nvp = vpa; }}/* * * Verify that a Perl SV is a string and save it in FreeRadius * Value Pair Format * */static int pairadd_sv(VALUE_PAIR **vp, char *key, SV *sv) { char *val; int val_len; VALUE_PAIR *vpp; if ((sv != NULL) && (SvPOK(sv))) { val = SvPV(sv, val_len); vpp = pairmake(key, val, T_OP_EQ); if (vpp != NULL) { pairadd(vp, vpp); radlog(L_DBG, "rlm_perl: Added pair %s = %s", key, val); return 1; } else { radlog(L_DBG, "rlm_perl: ERROR: Failed to create pair %s = %s", key, val); } } return 0;}/* * Boyan : * Gets the content from hashes */static int get_hv_content(HV *my_hv, VALUE_PAIR **vp){ SV *res_sv, **av_sv; AV *av; char *key; I32 key_len, len, i, j; int ret=0; for (i = hv_iterinit(my_hv); i > 0; i--) { res_sv = hv_iternextsv(my_hv,&key,&key_len); if (SvROK(res_sv) && (SvTYPE(SvRV(res_sv)) == SVt_PVAV)) { av = (AV*)SvRV(res_sv); len = av_len(av); for (j = 0; j <= len; j++) { av_sv = av_fetch(av, j, 0); ret = pairadd_sv(vp, key, *av_sv) + ret; } } else ret = pairadd_sv(vp, key, res_sv) + ret; } return ret;}/* * Call the function_name inside the module * Store all vps in hashes %RAD_CHECK %RAD_REPLY %RAD_REQUEST * */static int rlmperl_call(void *instance, REQUEST *request, char *function_name){ PERL_INST *inst = instance; VALUE_PAIR *vp; int exitstatus=0, count; STRLEN n_a; HV *rad_reply_hv; HV *rad_check_hv; HV *rad_request_hv;#ifdef USE_ITHREADS POOL_HANDLE *handle; if ((handle = pool_pop()) == NULL) { return RLM_MODULE_FAIL; } radlog(L_DBG,"found interpetator at address 0x%lx",(unsigned long) handle->clone); { dTHXa(handle->clone); PERL_SET_CONTEXT(handle->clone); }#endif { dSP; ENTER; SAVETMPS; /* * Radius has told us to call this function, but none * is defined. */ if (!function_name) { return RLM_MODULE_FAIL; } rad_reply_hv = get_hv("RAD_REPLY",1); rad_check_hv = get_hv("RAD_CHECK",1); rad_request_hv = get_hv("RAD_REQUEST",1); perl_store_vps(request->reply->vps, rad_reply_hv); perl_store_vps(request->config_items, rad_check_hv); perl_store_vps(request->packet->vps, rad_request_hv); vp = NULL; PUSHMARK(SP); /* * This way %RAD_xx can be pushed onto stack as sub parameters. * XPUSHs( newRV_noinc((SV *)rad_request_hv) ); * XPUSHs( newRV_noinc((SV *)rad_reply_hv) ); * XPUSHs( newRV_noinc((SV *)rad_check_hv) ); * PUTBACK; */ count = call_pv(function_name, G_SCALAR | G_EVAL | G_NOARGS); SPAGAIN; if (count == 1) { exitstatus = POPi; if (exitstatus >= 100 || exitstatus < 0) { exitstatus = RLM_MODULE_FAIL; } } PUTBACK; FREETMPS; LEAVE; if (SvTRUE(ERRSV)) { radlog(L_ERR, "rlm_perl: perl_embed:: module = %s , func = %s exit status= %s\n", inst->module, function_name, SvPV(ERRSV,n_a)); } if ((get_hv_content(rad_reply_hv, &vp)) > 0 ) { pairmove(&request->reply->vps, &vp); pairfree(&vp); } if ((get_hv_content(rad_check_hv, &vp)) > 0 ) { pairmove(&request->config_items, &vp); pairfree(&vp); }#if 0 /* * Do we want to allow this? */ if ((get_hv_content(rad_request_hv, &vp)) > 0 ) { pairfree(&request->packet->vps); request->packet->vps = vp; }#endif }#ifdef USE_ITHREADS pool_release(handle); radlog(L_DBG,"Unreserve perl at address 0x%lx", (unsigned long) handle->clone);#endif return exitstatus;}/* * 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 perl_authorize(void *instance, REQUEST *request){ return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_authorize);}/* * Authenticate the user with the given password. */static int perl_authenticate(void *instance, REQUEST *request){ return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_authenticate);}/* * Massage the request before recording it or proxying it */static int perl_preacct(void *instance, REQUEST *request){ return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_preacct);}/* * Write accounting information to this modules database. */static int perl_accounting(void *instance, REQUEST *request){ VALUE_PAIR *pair; int acctstatustype=0; if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) { acctstatustype = pair->lvalue; } else { radlog(L_ERR, "Invalid Accounting Packet"); return RLM_MODULE_INVALID; } switch (acctstatustype) { case PW_STATUS_START: if (((PERL_INST *)instance)->func_start_accounting) { return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_start_accounting); } else { return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_accounting); } break; case PW_STATUS_STOP: if (((PERL_INST *)instance)->func_stop_accounting) { return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_stop_accounting); } else { return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_accounting); } break; default: return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_accounting); }}/* * Check for simultaneouse-use */static int perl_checksimul(void *instance, REQUEST *request){ return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_checksimul);}/* * Detach a instance give a chance to a module to make some internal setup ... */static int perl_detach(void *instance){ PERL_INST *inst = (PERL_INST *) instance; int exitstatus=0,count=0;#ifdef USE_ITHREADS POOL_HANDLE *handle; for (handle = perl_pool.head; handle; handle = handle->next) { radlog(L_INFO,"Detach perl 0x%lx", (unsigned long) handle->clone); /* * Wait until clone becomes idle * */ while (handle->status == busy) { } /* * Give a clones chance to run detach function */ { dTHXa(handle->clone); PERL_SET_CONTEXT(handle->clone); { dSP; PUSHMARK(SP); count = call_pv(inst->func_detach, G_SCALAR | G_EVAL ); SPAGAIN; if (count == 1) { exitstatus = POPi; /* * FIXME: bug in perl * */ if (exitstatus >= 100 || exitstatus < 0) { exitstatus = RLM_MODULE_FAIL; } } PUTBACK; radlog(L_INFO,"detach at 0x%lx returned status %d", (unsigned long) handle->clone, exitstatus); } } } /* * * FIXME: For more efficienty we don't * free entire pool. We only reread config flags thus way * we can extend pool_size. * */ { dTHXa(interp); PERL_SET_CONTEXT(interp);#endif /* USE_ITHREADS */ { dSP; PUSHMARK(SP); count = call_pv(inst->func_detach, G_SCALAR | G_EVAL ); SPAGAIN; if (count == 1) { exitstatus = POPi; if (exitstatus >= 100 || exitstatus < 0) { exitstatus = RLM_MODULE_FAIL; } } PUTBACK; }#ifdef USE_ITHREADS }#endif xlat_unregister(inst->xlat_name, perl_xlat); free(inst->xlat_name); if (inst->func_authorize) free(inst->func_authorize); if (inst->func_authenticate) free(inst->func_authenticate); if (inst->func_accounting) free(inst->func_accounting); if (inst->func_preacct) free(inst->func_preacct); if (inst->func_checksimul) free(inst->func_checksimul); if (inst->func_detach) free(inst->func_detach); free(inst); return exitstatus;}/* * 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_perl = { "perl", /* Name */#ifdef USE_ITHREADS RLM_TYPE_THREAD_SAFE, /* type */#else RLM_TYPE_THREAD_UNSAFE,#endif perl_init, /* initialization */ perl_instantiate, /* instantiation */ { perl_authenticate, perl_authorize, perl_preacct, perl_accounting, perl_checksimul, /* check simul */ NULL, /* pre-proxy */ NULL, /* post-proxy */ NULL /* post-auth */ }, perl_detach, /* detach */ NULL, /* destroy */};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -