#include <freeradius-devel/ident.h>
RCSID("$Id: rlm_ruby.c,v 0.1 2008/10/22 12:54:00 antti Exp $")
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
#include <ruby.h>
typedef struct rlm_ruby_t {
#define RLM_RUBY_STRUCT(foo) int func_##foo
RLM_RUBY_STRUCT(instantiate);
RLM_RUBY_STRUCT(authorize);
RLM_RUBY_STRUCT(authenticate);
RLM_RUBY_STRUCT(preacct);
RLM_RUBY_STRUCT(accounting);
RLM_RUBY_STRUCT(checksimul);
RLM_RUBY_STRUCT(preproxy);
RLM_RUBY_STRUCT(postproxy);
RLM_RUBY_STRUCT(postauth);
#ifdef WITH_COA
RLM_RUBY_STRUCT(recvcoa);
RLM_RUBY_STRUCT(sendcoa);
#endif
RLM_RUBY_STRUCT(detach);
char *scriptFile;
char *moduleName;
VALUE pModule_builtin;
} rlm_ruby_t;
static const CONF_PARSER module_config[] = {
{ "scriptfile", PW_TYPE_FILENAME,
offsetof(struct rlm_ruby_t, scriptFile), NULL, NULL},
{ "modulename", PW_TYPE_STRING_PTR,
offsetof(struct rlm_ruby_t, moduleName), NULL, "Radiusd"},
{ NULL, -1, 0, NULL, NULL}
};
static VALUE radlog_rb(VALUE self, VALUE msg_type, VALUE rb_msg) {
int status;
char *msg;
status = FIX2INT(msg_type);
msg = STR2CSTR(rb_msg);
radlog(status, msg);
return Qnil;
}
static void add_vp_tuple(VALUE_PAIR **vpp, VALUE rb_value,
const char *function_name) {
int i, outertuplesize;
VALUE_PAIR *vp;
if (NIL_P(rb_value)) {
return;
}
if (TYPE(rb_value) != T_ARRAY) {
radlog(L_ERR, "add_vp_tuple, %s: non-array passed", function_name);
return;
}
outertuplesize = RARRAY(rb_value)->len;
for (i = 0; i < outertuplesize; i++) {
VALUE pTupleElement = rb_ary_entry(rb_value, i);
if ((pTupleElement != 0) &&
(TYPE(pTupleElement) == T_ARRAY)) {
int tuplesize;
if ((tuplesize = RARRAY(pTupleElement)->len) != 2) {
radlog(L_ERR, "%s: tuple element %d is a tuple "
" of size %d. must be 2\n", function_name,
i, tuplesize);
} else {
VALUE pString1, pString2;
pString1 = rb_ary_entry(pTupleElement, 0);
pString2 = rb_ary_entry(pTupleElement, 1);
if ((TYPE(pString1) == T_STRING) &&
(TYPE(pString2) == T_STRING)) {
const char *s1, *s2;
s1 = STR2CSTR(pString1);
s2 = STR2CSTR(pString2);
if ((s1 != NULL) && (s2 != NULL)) {
radlog(L_DBG, "%s: %s = %s ",
function_name, s1, s2);
vp = pairmake(s1, s2, T_OP_EQ);
if (vp != NULL) {
pairadd(vpp, vp);
radlog(L_DBG, "%s: s1, s2 OK\n",
function_name);
} else {
radlog(L_DBG, "%s: s1, s2 FAILED\n",
function_name);
}
} else {
radlog(L_ERR, "%s: string conv failed\n",
function_name);
}
} else {
radlog(L_ERR, "%s: tuple element %d must be "
"(string, string)", function_name, i);
}
}
} else {
radlog(L_ERR, "%s: tuple element %d is not a tuple\n",
function_name, i);
}
}
}
static int ruby_function(REQUEST *request, int func, VALUE module, const char *function_name) {
#define BUF_SIZE 1024
char buf[BUF_SIZE];
VALUE_PAIR *vp;
VALUE rb_request, rb_result, rb_reply_items, rb_config_items;
int n_tuple, return_value;
radlog(L_DBG, "Calling ruby function %s which has id: %d\n", function_name, func);
if (func == 0) {
return RLM_MODULE_OK;
}
return_value = RLM_MODULE_OK;
n_tuple = 0;
if (request != NULL) {
for (vp = request->packet->vps; vp; vp = vp->next) {
n_tuple++;
}
}
rb_request = rb_ary_new2(n_tuple);
if (request != NULL) {
for (vp = request->packet->vps; vp; vp = vp->next) {
VALUE tmp = rb_ary_new2(2);
if (vp->flags.has_tag) {
snprintf(buf, BUF_SIZE, "%s:%d", vp->name, vp->flags.tag);
} else {
strcpy(buf, vp->name);
}
VALUE rbString1 = rb_str_new2(buf);
vp_prints_value(buf, sizeof (buf), vp, 1);
VALUE rbString2 = rb_str_new2(buf);
rb_ary_push(tmp, rbString1);
rb_ary_push(tmp, rbString2);
rb_ary_push(rb_request, tmp);
}
}
rb_result = rb_funcall(module, func, 1, rb_request);
if (TYPE(rb_result) == T_ARRAY) {
if (!FIXNUM_P(rb_ary_entry(rb_result, 0))) {
radlog(L_ERR, "First element of an array was not a FIXNUM(Which has to be a return_value)");
} else
return_value = FIX2INT(rb_ary_entry(rb_result, 0));
rb_reply_items = rb_ary_entry(rb_result, 1);
rb_config_items = rb_ary_entry(rb_result, 2);
add_vp_tuple(&request->reply->vps, rb_reply_items, function_name);
add_vp_tuple(&request->config_items, rb_config_items, function_name);
} else if (FIXNUM_P(rb_result)) {
return_value = FIX2INT(rb_result);
}
return return_value;
}
static struct varlookup {
const char* name;
int value;
} constants[] = {
{ "L_DBG", L_DBG},
{ "L_AUTH", L_AUTH},
{ "L_INFO", L_INFO},
{ "L_ERR", L_ERR},
{ "L_PROXY", L_PROXY},
{ "L_CONS", L_CONS},
{ "RLM_MODULE_REJECT", RLM_MODULE_REJECT},
{ "RLM_MODULE_FAIL", RLM_MODULE_FAIL},
{ "RLM_MODULE_OK", RLM_MODULE_OK},
{ "RLM_MODULE_HANDLED", RLM_MODULE_HANDLED},
{ "RLM_MODULE_INVALID", RLM_MODULE_INVALID},
{ "RLM_MODULE_USERLOCK", RLM_MODULE_USERLOCK},
{ "RLM_MODULE_NOTFOUND", RLM_MODULE_NOTFOUND},
{ "RLM_MODULE_NOOP", RLM_MODULE_NOOP},
{ "RLM_MODULE_UPDATED", RLM_MODULE_UPDATED},
{ "RLM_MODULE_NUMCODES", RLM_MODULE_NUMCODES},
{ NULL, 0},
};
static int load_ruby_function(const char *f_name, int *func, VALUE module) {
if (f_name == NULL) {
*func = 0;
} else {
*func = rb_intern(f_name);
if (!rb_respond_to(module, *func))
*func = 0;
}
radlog(L_DBG, "load_ruby_function %s, result: %d", f_name, *func);
return 0;
}
static int ruby_instantiate(CONF_SECTION *conf, void **instance) {
rlm_ruby_t *data;
VALUE module;
int idx;
radlog(L_DBG, "[rlm_ruby]: ruby_instantiate");
ruby_init();
ruby_init_loadpath();
ruby_script("radiusd");
#warning FIXME: Disabling GC, it will eat your memory, but at least it will be stable.
rb_gc_disable();
int status;
data = rad_malloc(sizeof (*data));
if (!data) {
return -1;
}
memset(data, 0, sizeof (*data));
if (cf_section_parse(conf, data, module_config) < 0) {
free(data);
return -1;
}
if ((module = data->pModule_builtin = rb_define_module(data->moduleName)) == 0) {
radlog(L_ERR, "Ruby rb_define_module failed");
free(data);
return -1;
}
for (idx = 0; constants[idx].name; idx++)
rb_define_const(module, constants[idx].name, INT2NUM(constants[idx].value));
rb_define_module_function(module, "radlog", radlog_rb, 2);
if (data->scriptFile == NULL) {
radlog(L_ERR, "Script File was not set");
} else {
radlog(L_DBG, "Loading file %s...", data->scriptFile);
rb_load_protect(rb_str_new2(data->scriptFile), 0, &status);
if (!status)
radlog(L_DBG, "Loaded file %s", data->scriptFile);
else
radlog(L_ERR, "Error loading file %s status: %d", data->scriptFile, status);
}
#define RLM_RUBY_LOAD(foo) if (load_ruby_function(#foo, &data->func_##foo, data->pModule_builtin)==-1) { \
\
return -1; \
}
RLM_RUBY_LOAD(instantiate);
RLM_RUBY_LOAD(authenticate);
RLM_RUBY_LOAD(authorize);
RLM_RUBY_LOAD(preacct);
RLM_RUBY_LOAD(accounting);
RLM_RUBY_LOAD(checksimul);
RLM_RUBY_LOAD(preproxy);
RLM_RUBY_LOAD(postproxy);
RLM_RUBY_LOAD(postauth);
#ifdef WITH_COA
RLM_RUBY_LOAD(recvcoa);
RLM_RUBY_LOAD(sendcoa);
#endif
RLM_RUBY_LOAD(detach);
*instance = data;
return ruby_function(NULL, data->func_instantiate, data->pModule_builtin, "instantiate");
}
#define RLM_RUBY_FUNC(foo) static int ruby_##foo(void *instance, REQUEST *request) \
{ \
return ruby_function(request, \
((struct rlm_ruby_t *)instance)->func_##foo,((struct rlm_ruby_t *)instance)->pModule_builtin, \
#foo); \
}
RLM_RUBY_FUNC(authorize)
RLM_RUBY_FUNC(authenticate)
RLM_RUBY_FUNC(preacct)
RLM_RUBY_FUNC(accounting)
RLM_RUBY_FUNC(checksimul)
RLM_RUBY_FUNC(preproxy)
RLM_RUBY_FUNC(postproxy)
RLM_RUBY_FUNC(postauth)
#ifdef WITH_COA
RLM_RUBY_FUNC(recvcoa)
RLM_RUBY_FUNC(sendcoa)
#endif
static int ruby_detach(void *instance) {
int return_value;
return_value = -1;
free(instance);
ruby_finalize();
ruby_cleanup(0);
radlog(L_DBG, "ruby_detach done");
return_value = RLM_MODULE_OK;
return return_value;
}
module_t rlm_ruby = {
RLM_MODULE_INIT,
"ruby",
RLM_TYPE_THREAD_UNSAFE,
ruby_instantiate,
ruby_detach,
{
ruby_authenticate,
ruby_authorize,
ruby_preacct,
ruby_accounting,
ruby_checksimul,
ruby_preproxy,
ruby_postproxy,
ruby_postauth
#ifdef WITH_COA
, ruby_recvcoa,
ruby_sendcoa
#endif
},
};