2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2 if the
4 * License as published by the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 * @brief Translates requests between the server an a ruby interpreter.
21 * @copyright 2008 Andriy Dmytrenko aka Antti, BuzhNET
24 #include <freeradius-devel/ident.h>
28 #include <freeradius-devel/radiusd.h>
29 #include <freeradius-devel/modules.h>
32 * Undefine any HAVE_* flags which may conflict
33 * ruby.h *REALLY* shouldn't #include its config.h file,
41 * Define a structure for our module configuration.
43 * These variables do not need to be in a structure, but it's
44 * a lot cleaner to do so, and a pointer to the structure can
45 * be used as the instance handle.
47 typedef struct rlm_ruby_t {
48 #define RLM_RUBY_STRUCT(foo) int func_##foo
50 RLM_RUBY_STRUCT(instantiate);
51 RLM_RUBY_STRUCT(authorize);
52 RLM_RUBY_STRUCT(authenticate);
53 RLM_RUBY_STRUCT(preacct);
54 RLM_RUBY_STRUCT(accounting);
55 RLM_RUBY_STRUCT(checksimul);
56 RLM_RUBY_STRUCT(preproxy);
57 RLM_RUBY_STRUCT(postproxy);
58 RLM_RUBY_STRUCT(postauth);
60 RLM_RUBY_STRUCT(recvcoa);
61 RLM_RUBY_STRUCT(sendcoa);
63 RLM_RUBY_STRUCT(detach);
67 VALUE pModule_builtin;
72 * A mapping of configuration file names to internal variables.
74 * Note that the string is dynamically allocated, so it MUST
75 * be freed. When the configuration file parse re-reads the string,
76 * it free's the old one, and strdup's the new one, placing the pointer
77 * to the strdup'd string into 'config.string'. This gets around
80 static const CONF_PARSER module_config[] = {
81 { "scriptfile", PW_TYPE_FILENAME,
82 offsetof(struct rlm_ruby_t, scriptFile), NULL, NULL},
83 { "modulename", PW_TYPE_STRING_PTR,
84 offsetof(struct rlm_ruby_t, moduleName), NULL, "Radiusd"},
85 { NULL, -1, 0, NULL, NULL} /* end of module_config */
90 * radiusd Ruby functions
95 static VALUE radlog_rb(UNUSED VALUE self, VALUE msg_type, VALUE rb_msg) {
98 status = FIX2INT(msg_type);
99 msg = StringValuePtr(rb_msg);
100 radlog(status, "%s", msg);
104 /* Tuple to value pair conversion */
106 static void add_vp_tuple(VALUE_PAIR **vpp, VALUE rb_value,
107 const char *function_name) {
108 int i, outertuplesize;
111 /* If the Ruby function gave us nil for the tuple, then just return. */
112 if (NIL_P(rb_value)) {
116 if (TYPE(rb_value) != T_ARRAY) {
117 radlog(L_ERR, "add_vp_tuple, %s: non-array passed", function_name);
121 /* Get the array size. */
122 outertuplesize = RARRAY_LEN(rb_value);
124 for (i = 0; i < outertuplesize; i++) {
125 VALUE pTupleElement = rb_ary_entry(rb_value, i);
127 if ((pTupleElement != 0) &&
128 (TYPE(pTupleElement) == T_ARRAY)) {
130 /* Check if it's a pair */
133 if ((tuplesize = RARRAY_LEN(pTupleElement)) != 2) {
134 radlog(L_ERR, "%s: tuple element %d is a tuple "
135 " of size %d. must be 2\n", function_name,
138 VALUE pString1, pString2;
140 pString1 = rb_ary_entry(pTupleElement, 0);
141 pString2 = rb_ary_entry(pTupleElement, 1);
143 if ((TYPE(pString1) == T_STRING) &&
144 (TYPE(pString2) == T_STRING)) {
149 /* pairmake() will convert and find any
150 * errors in the pair.
153 s1 = StringValuePtr(pString1);
154 s2 = StringValuePtr(pString2);
156 if ((s1 != NULL) && (s2 != NULL)) {
157 radlog(L_DBG, "%s: %s = %s ",
158 function_name, s1, s2);
160 /* xxx Might need to support other T_OP */
161 vp = pairmake(s1, s2, T_OP_EQ);
164 radlog(L_DBG, "%s: s1, s2 OK\n",
167 radlog(L_DBG, "%s: s1, s2 FAILED\n",
171 radlog(L_ERR, "%s: string conv failed\n",
176 radlog(L_ERR, "%s: tuple element %d must be "
177 "(string, string)", function_name, i);
181 radlog(L_ERR, "%s: tuple element %d is not a tuple\n",
188 /* This is the core Ruby function that the others wrap around.
189 * Pass the value-pair print strings in a tuple.
190 * xxx We're not checking the errors. If we have errors, what do we do?
193 #define BUF_SIZE 1024
194 static rlm_rcode_t ruby_function(REQUEST *request, int func,
195 VALUE module, const char *function_name) {
197 rlm_rcode_t rcode = RLM_MODULE_OK;
199 char buf[BUF_SIZE]; /* same size as vp_print buffer */
202 VALUE rb_request, rb_result, rb_reply_items, rb_config_items;
205 radlog(L_DBG, "Calling ruby function %s which has id: %d\n", function_name, func);
207 /* Return with "OK, continue" if the function is not defined.
208 * TODO: Should check with rb_respond_to each time, just because ruby can define function dynamicly?
217 for (vp = request->packet->vps; vp; vp = vp->next) {
224 Creating ruby array, that contains arrays of [name,value]
225 Maybe we should use hash instead? Can this names repeat?
227 rb_request = rb_ary_new2(n_tuple);
229 for (vp = request->packet->vps; vp; vp = vp->next) {
230 VALUE tmp = rb_ary_new2(2);
232 /* The name. logic from vp_prints, lib/print.c */
233 if (vp->da->flags.has_tag) {
234 snprintf(buf, BUF_SIZE, "%s:%d", vp->da->name, vp->tag);
236 strlcpy(buf, vp->da->name, sizeof(buf));
238 VALUE rbString1 = rb_str_new2(buf);
239 /* The value. Use delimiter - don't know what that means */
240 vp_prints_value(buf, sizeof (buf), vp, 1);
241 VALUE rbString2 = rb_str_new2(buf);
243 rb_ary_push(tmp, rbString1);
244 rb_ary_push(tmp, rbString2);
245 rb_ary_push(rb_request, tmp);
249 /* Calling corresponding ruby function, passing request and catching result */
250 rb_result = rb_funcall(module, func, 1, rb_request);
253 * Checking result, it can be array of type [result,
254 * [array of reply pairs],[array of config pairs]],
255 * It can also be just a fixnum, which is a result itself.
257 if (TYPE(rb_result) == T_ARRAY) {
258 if (!FIXNUM_P(rb_ary_entry(rb_result, 0))) {
259 radlog(L_ERR, "rlm_ruby: First element of an array was not a "
260 "FIXNUM (Which has to be a return_value)");
262 rcode = RLM_MODULE_FAIL;
266 rcode = FIX2INT(rb_ary_entry(rb_result, 0));
269 * Only process the results if we were passed a request.
272 rb_reply_items = rb_ary_entry(rb_result, 1);
273 rb_config_items = rb_ary_entry(rb_result, 2);
275 add_vp_tuple(&request->reply->vps, rb_reply_items,
277 add_vp_tuple(&request->config_items, rb_config_items,
280 } else if (FIXNUM_P(rb_result)) {
281 rcode = FIX2INT(rb_result);
288 static struct varlookup {
296 { "L_PROXY", L_PROXY},
297 { "RLM_MODULE_REJECT", RLM_MODULE_REJECT},
298 { "RLM_MODULE_FAIL", RLM_MODULE_FAIL},
299 { "RLM_MODULE_OK", RLM_MODULE_OK},
300 { "RLM_MODULE_HANDLED", RLM_MODULE_HANDLED},
301 { "RLM_MODULE_INVALID", RLM_MODULE_INVALID},
302 { "RLM_MODULE_USERLOCK", RLM_MODULE_USERLOCK},
303 { "RLM_MODULE_NOTFOUND", RLM_MODULE_NOTFOUND},
304 { "RLM_MODULE_NOOP", RLM_MODULE_NOOP},
305 { "RLM_MODULE_UPDATED", RLM_MODULE_UPDATED},
306 { "RLM_MODULE_NUMCODES", RLM_MODULE_NUMCODES},
311 * Import a user module and load a function from it
313 static int load_ruby_function(const char *f_name, int *func, VALUE module) {
314 if (f_name == NULL) {
317 *func = rb_intern(f_name);
318 /* rb_intern returns a symbol of a function, not a function itself
319 it can be aplied to any recipient,
320 so we should check it for our module recipient
322 if (!rb_respond_to(module, *func))
325 radlog(L_DBG, "load_ruby_function %s, result: %d", f_name, *func);
330 * Do any per-module initialization that is separate to each
331 * configured instance of the module. e.g. set up connections
332 * to external databases, read configuration files, set up
333 * dictionary entries, etc.
335 * If configuration information is given in the config section
336 * that must be referenced in later calls, store a handle to it
337 * in *instance otherwise put a null pointer there.
340 static int ruby_instantiate(CONF_SECTION *conf, void **instance)
347 * Initialize Ruby interpreter. Fatal error if this fails.
351 ruby_init_loadpath();
352 ruby_script("radiusd");
353 /* disabling GC, it will eat your memory, but at least it will be stable. */
359 * Set up a storage area for instance data
361 *instance = data = talloc_zero(conf, rlm_ruby_t);
362 if (!data) return -1;
365 * If the configuration parameters can't be parsed, then
368 if (cf_section_parse(conf, data, module_config) < 0) {
373 * Setup our 'radiusd' module.
376 if ((module = data->pModule_builtin = rb_define_module(data->moduleName)) == 0) {
377 radlog(L_ERR, "Ruby rb_define_module failed");
381 * Load constants into module
383 for (idx = 0; constants[idx].name; idx++)
384 rb_define_const(module, constants[idx].name, INT2NUM(constants[idx].value));
386 /* Add functions into module */
387 rb_define_module_function(module, "radlog", radlog_rb, 2);
389 if (data->scriptFile == NULL) {
390 /* TODO: What actualy should we do? Exit with module fail? Or continue... but what the point then? */
391 radlog(L_ERR, "Script File was not set");
393 radlog(L_DBG, "Loading file %s...", data->scriptFile);
394 rb_load_protect(rb_str_new2(data->scriptFile), 0, &status);
396 radlog(L_DBG, "Loaded file %s", data->scriptFile);
398 radlog(L_ERR, "Error loading file %s status: %d", data->scriptFile, status);
401 * Import user modules.
403 #define RLM_RUBY_LOAD(foo) if (load_ruby_function(#foo, &data->func_##foo, data->pModule_builtin)==-1) { \
407 RLM_RUBY_LOAD(instantiate);
408 RLM_RUBY_LOAD(authenticate);
409 RLM_RUBY_LOAD(authorize);
410 RLM_RUBY_LOAD(preacct);
411 RLM_RUBY_LOAD(accounting);
412 RLM_RUBY_LOAD(checksimul);
413 RLM_RUBY_LOAD(preproxy);
414 RLM_RUBY_LOAD(postproxy);
415 RLM_RUBY_LOAD(postauth);
417 RLM_RUBY_LOAD(recvcoa);
418 RLM_RUBY_LOAD(sendcoa);
420 RLM_RUBY_LOAD(detach);
422 /* Call the instantiate function. No request. Use the return value. */
423 return ruby_function(NULL, data->func_instantiate, data->pModule_builtin, "instantiate");
426 #define RLM_RUBY_FUNC(foo) static rlm_rcode_t ruby_##foo(void *instance, REQUEST *request) \
428 return ruby_function(request, \
429 ((struct rlm_ruby_t *)instance)->func_##foo,((struct rlm_ruby_t *)instance)->pModule_builtin, \
433 RLM_RUBY_FUNC(authorize)
434 RLM_RUBY_FUNC(authenticate)
435 RLM_RUBY_FUNC(preacct)
436 RLM_RUBY_FUNC(accounting)
437 RLM_RUBY_FUNC(checksimul)
438 RLM_RUBY_FUNC(preproxy)
439 RLM_RUBY_FUNC(postproxy)
440 RLM_RUBY_FUNC(postauth)
442 RLM_RUBY_FUNC(recvcoa)
443 RLM_RUBY_FUNC(sendcoa)
446 static int ruby_detach(UNUSED void *instance)
455 * The module name should be the only globally exported symbol.
456 * That is, everything else should be 'static'.
458 * If the module needs to temporarily modify it's instantiation
459 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
460 * The server will then take care of ensuring that the module
461 * is single-threaded.
463 module_t rlm_ruby = {
466 // RLM_TYPE_THREAD_SAFE, /* type */
467 RLM_TYPE_THREAD_UNSAFE, /* type, ok, let's be honest, MRI is not yet treadsafe */
468 ruby_instantiate, /* instantiation */
469 ruby_detach, /* detach */
471 ruby_authenticate, /* authentication */
472 ruby_authorize, /* authorization */
473 ruby_preacct, /* preaccounting */
474 ruby_accounting, /* accounting */
475 ruby_checksimul, /* checksimul */
476 ruby_preproxy, /* pre-proxy */
477 ruby_postproxy, /* post-proxy */
478 ruby_postauth /* post-auth */