25 RCSID(
"$Id: 853bde97c05416d2b4df74c6f09b3a019a81517e $")
27 #include <freeradius-devel/server/base.h>
28 #include <freeradius-devel/server/module_rlm.h>
29 #include <freeradius-devel/util/htrie.h>
30 #include <freeradius-devel/util/debug.h>
32 #include <freeradius-devel/server/map_proc.h>
35 fr_value_box_list_t *key, map_list_t
const *maps);
100 *
out = strchr(buf + 1, *
inst->delimiter);
103 for (p = buf; *p !=
'\0'; p++) {
127 if ((*p ==
'"') && (p[1] ==
'"')) {
136 if ((*p ==
'"') && (p[1] <
' ')) {
146 if ((*p ==
'"') && (p[1] == *
inst->delimiter)) {
164 static int8_t
csv_cmp(
void const *one,
void const *two)
196 if (!
inst->allow_multiple_keys && !
inst->multiple_index_fields) {
211 cf_log_err(
conf,
"Failed inserting entry for file %s line %d: %s",
229 sizeof(*e) + (
inst->used_fields *
sizeof(e->
data[0]))));
233 if (!e->
key)
goto fail;
236 cf_log_err(
conf,
"Failed parsing key field in file %s line %d - %s",
inst->filename, lineno,
246 for (i = 0; i <
inst->used_fields; i++) {
263 sizeof(*e) + (
inst->used_fields *
sizeof(e->
data[0]))));
266 for (p =
buffer, i = 0; p != NULL; p = q, i++) {
272 if (q) *(q++) =
'\0';
274 if (i >=
inst->num_fields) {
282 if (i ==
inst->index_field) {
288 if (
inst->multiple_index_fields) {
319 if (!e->
key)
goto fail;
322 p, strlen(p), NULL,
false) < 0) {
323 cf_log_err(
conf,
"Failed parsing key field in file %s line %d - %s",
inst->filename, lineno,
335 if (
inst->field_offsets[i] < 0)
continue;
345 p, strlen(p), NULL,
false) < 0) {
346 cf_log_err(
conf,
"Failed parsing field '%s' in file %s line %d - %s",
inst->field_names[i],
357 if (i < inst->num_fields) {
358 cf_log_err(
conf,
"Too few fields in file %s at line %d (%d < %d)",
inst->filename, lineno, i,
inst->num_fields);
376 for (i = 0; i <
inst->num_fields; i++) {
377 if (strcmp(field_name,
inst->field_names[i]) == 0) {
378 if (array_offset) *array_offset = i;
379 return inst->field_offsets[i];
386 #define CSV_MAX_ATTRMAP (128)
401 switch (map->
lhs->type) {
411 cf_log_err(map->
ci,
"Left hand side of map must be an attribute or list, not a %s",
420 switch (map->
rhs->type) {
440 if (
inst->field_types[offset] !=
type) {
441 cf_log_err(map->
ci,
"Field '%s' maps to two different data types, making it impossible to parse it.", map->
rhs->name);
452 cf_log_err(map->
ci,
"Right hand side of map must be a field name, not a %s",
473 cf_log_err(map->
ci,
"Operator \"%s\" not allowed for CSV mappings",
485 tmpl_t const *src, map_list_t
const *maps)
487 map_t const *map = NULL;
495 while ((map = map_list_next(maps, map))) {
525 if (
inst->delimiter[1]) {
532 switch (
inst->key_data_type) {
537 cf_log_err(
conf,
"Can't determine key data_type. 'key' must be an expression of type "
538 "xlat, attr, or data");
570 if ((*
inst->index_field_name ==
',') || (*
inst->index_field_name == *
inst->delimiter)) {
571 cf_log_err(
conf,
"Field names cannot begin with the '%c' character", *
inst->index_field_name);
575 if (
inst->delimiter[1] !=
'\0') {
576 cf_log_err(
conf,
"The 'delimiter' field MUST be one character long");
587 fp = fopen(
inst->filename,
"r");
602 if (!q)
goto error_eof;
618 for (p =
inst->fields; p != NULL; p = strchr(p + 1, *
inst->delimiter)) {
622 if (
inst->num_fields < 2) {
623 cf_log_err(
conf,
"The CSV file MUST have at least a key field and data field");
627 MEM(
inst->field_names = talloc_zero_array(
inst,
const char *,
inst->num_fields));
631 for (i = 0; i <
inst->num_fields; i++) {
632 inst->field_offsets[i] = -1;
645 inst->index_field = -1;
658 while (*q && (*q != *
inst->delimiter)) {
659 if ((*q ==
'\'') || (*q ==
'"')) {
697 if (last_field && (*p ==
',') && (strcmp(p + 1,
inst->index_field_name) == 0)) {
698 inst->index_field = i;
701 inst->multiple_index_fields =
true;
703 }
else if (strcmp(p,
inst->index_field_name) == 0) {
704 inst->index_field = i;
706 }
else if ((*p ==
',') || (*p == *
inst->delimiter)) {
707 cf_log_err(
conf,
"Field names MUST NOT begin with the '%c' character", *p);
711 inst->field_offsets[i] =
inst->used_fields++;
717 inst->field_names[i] = p;
719 if (last_field)
break;
726 if (
inst->index_field < 0) {
727 cf_log_err(
conf,
"index_field '%s' does not appear in the list of field names",
728 inst->index_field_name);
735 inst->field_types[
inst->index_field] =
inst->key_data_type;
768 map_list_init(&
inst->map);
778 cf_log_err(
conf,
"There is no 'key' defined for the 'update' section");
792 }
else if (
inst->key) {
793 cf_log_warn(
conf,
"Ignoring 'key', as no 'update' section has been defined.");
799 fp = fopen(
inst->filename,
"r");
842 char const *str =
uctx;
855 RWDEBUG(
"Failed expanding string");
862 RWDEBUG(
"No such attribute '%s'", attr);
903 map_t const *map = NULL;
913 while ((map = map_list_next(maps, map))) {
921 if (
tmpl_aexpand(request, &field_name, request, map->
rhs, NULL, NULL) < 0) {
923 REDEBUG(
"Failed expanding RHS at %s", map->
lhs->name);
937 REDEBUG(
"No such field name %s", map->
rhs->name);
979 fr_value_box_list_t *key, map_list_t
const *maps)
985 REDEBUG(
"CSV key cannot be (null)");
991 key_head, key,
inst->key_data_type,
1018 DEBUG(
"Failed expanding key '%s'",
inst->key->name);
1026 if (key->type !=
inst->key_data_type) {
1035 DEBUG(
"Failed casting %pV to data type '%s'",
1041 RDEBUG2(
"Processing CVS map with key %pV", key);
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
static int const char char buffer[256]
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
#define CONF_PARSER_TERMINATOR
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
@ CONF_FLAG_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
@ CONF_FLAG_FILE_INPUT
File matching value must exist, and must be readable.
@ CONF_FLAG_NOT_EMPTY
CONF_PAIR is required to have a non zero length value.
Defines a CONF_PAIR to C data type mapping.
A section grouping multiple CONF_PAIR.
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
#define cf_log_err(_cf, _fmt,...)
#define cf_log_warn(_cf, _fmt,...)
fr_dcursor_eval_t void const * uctx
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
uint32_t(* fr_hash_t)(void const *)
fr_htrie_t * fr_htrie_alloc(TALLOC_CTX *ctx, fr_htrie_type_t type, fr_hash_t hash_data, fr_cmp_t cmp_data, fr_trie_key_t get_key, fr_free_t free_data)
An abstraction over our internal hashes, rb trees, and prefix tries.
static fr_htrie_type_t fr_htrie_hint(fr_type_t type)
static bool fr_htrie_insert(fr_htrie_t *ht, void const *data)
Insert data into a htrie.
static void * fr_htrie_find(fr_htrie_t *ht, void const *data)
Find data in a htrie.
A hash/rb/prefix trie abstraction.
#define REXDENT()
Exdent (unindent) R* messages by one level.
#define RPWDEBUG(fmt,...)
#define RINDENT()
Indent R* messages by one level.
int map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, CONF_SECTION *cs, tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules, map_validate_t validate, void *uctx, unsigned int max)
Convert a config section into an attribute map.
int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t func, void *ctx)
Convert map_t to fr_pair_t (s) and add them to a request_t.
int map_proc_register(TALLOC_CTX *ctx, void const *mod_inst, char const *name, map_proc_func_t evaluate, map_proc_instantiate_t instantiate, size_t inst_size, fr_value_box_safe_for_t literals_safe_for)
Register a map processor.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_VALUE_BOX
A boxed value.
@ FR_TYPE_OCTETS
Raw octets.
int8_t(* fr_cmp_t)(void const *a, void const *b)
module_instance_t const * mi
Instance of the module being instantiated.
module_instance_t * mi
Instance of the module being instantiated.
Temporary structure to hold arguments for module calls.
Temporary structure to hold arguments for instantiation calls.
module_t common
Common fields presented by all modules.
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
int fr_pair_value_from_str(fr_pair_t *vp, char const *value, size_t inlen, fr_sbuff_unescape_rules_t const *uerules, bool tainted)
Convert string value to native attribute value.
static const conf_parser_t config[]
The main red black tree structure.
#define RETURN_MODULE_NOOP
#define RETURN_MODULE_RCODE(_rcode)
rlm_rcode_t
Return codes indicating the result of the module call.
@ RLM_MODULE_FAIL
Module failed, don't reply.
@ RLM_MODULE_UPDATED
OK (pairs modified).
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
static int8_t csv_cmp(void const *one, void const *two)
static unlang_action_t mod_process(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
static int csv_maps_verify(CONF_SECTION *cs, void const *mod_inst, UNUSED void *proc_inst, tmpl_t const *src, map_list_t const *maps)
char const * index_field_name
char const ** field_names
static int fieldname2offset(rlm_csv_t const *inst, char const *field_name, int *array_offset)
static bool buf2entry(rlm_csv_t *inst, char *buf, char **out)
static bool file2csv(CONF_SECTION *conf, rlm_csv_t *inst, int lineno, char *buffer)
static int csv_to_key(uint8_t **out, size_t *outlen, void const *data)
static int mod_bootstrap(module_inst_ctx_t const *mctx)
static int csv_map_getvalue(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, void *uctx)
static uint32_t csv_hash(void const *data)
static bool insert_entry(CONF_SECTION *conf, rlm_csv_t *inst, rlm_csv_entry_t *e, int lineno)
bool multiple_index_fields
map_list_t map
if there is an "update" section in the configuration.
static bool duplicate_entry(CONF_SECTION *conf, rlm_csv_t *inst, rlm_csv_entry_t *old, char *p, int lineno)
static int csv_map_verify(map_t *map, void *instance)
static rlm_rcode_t mod_map_apply(rlm_csv_t const *inst, request_t *request, fr_value_box_t const *key, map_list_t const *maps)
Perform a search and map the result of the search to server attributes.
static const conf_parser_t module_config[]
static int mod_instantiate(module_inst_ctx_t const *mctx)
Instantiate the module.
static unlang_action_t mod_map_proc(rlm_rcode_t *p_result, void const *mod_inst, UNUSED void *proc_inst, request_t *request, fr_value_box_list_t *key, map_list_t const *maps)
Perform a search and map the result of the search to server attributes.
static int instantiate(module_inst_ctx_t const *mctx)
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
char const * name
Instance name e.g. user_database.
@ MODULE_TYPE_DYNAMIC_UNSAFE
Instances of this module cannot be created at runtime.
CONF_SECTION * conf
Module's instance configuration.
void * data
Module's instance data.
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Named methods exported by a module.
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
#define tmpl_is_attr(vpt)
#define tmpl_aexpand_type(_ctx, _out, _type, _request, _vpt, _escape, _escape_ctx)
Expand a tmpl to a C type, allocing a new buffer to hold the string.
@ TMPL_TYPE_ATTR_UNRESOLVED
An attribute reference that we couldn't resolve but looked valid.
@ TMPL_TYPE_ATTR
Reference to one or more attributes.
@ TMPL_TYPE_DATA_UNRESOLVED
Unparsed literal string.
#define tmpl_is_data_unresolved(vpt)
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
static char const * tmpl_attr_tail_unresolved(tmpl_t const *vpt)
Return the last attribute reference unresolved da.
#define tmpl_aexpand(_ctx, _out, _request, _vpt, _escape, _escape_ctx)
Expand a tmpl to a C type, allocing a new buffer to hold the string.
fr_type_t tmpl_expanded_type(tmpl_t const *vpt)
Return the native data type of the expression.
static char const * tmpl_type_to_str(tmpl_type_t type)
Return a static string containing the type name.
Optional arguments passed to vp_tmpl functions.
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
fr_token_t op
The operator that controls insertion of the dst attribute.
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
CONF_ITEM * ci
Config item that the map was created from.
uint8_t allow_foreign
Allow arguments not found in dict_def.
Stores an attribute, a value and various bits of other data.
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
#define talloc_get_type_abort_const
fr_table_num_ordered_t const fr_tokens_table[]
int(* fr_trie_key_t)(uint8_t **out, size_t *outlen, void const *data)
char const * fr_strerror(void)
Get the last library error.
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
#define fr_type_is_null(_x)
ssize_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, char const *in, size_t inlen, fr_sbuff_unescape_rules_t const *erules, bool tainted)
uint32_t fr_value_box_hash(fr_value_box_t const *vb)
Hash the contents of a value box.
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
int8_t fr_value_box_cmp(fr_value_box_t const *a, fr_value_box_t const *b)
Compare two values.
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
int fr_value_box_to_key(uint8_t **out, size_t *outlen, fr_value_box_t const *value)
Get a key from a value box.
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
int fr_value_box_list_concat_in_place(TALLOC_CTX *ctx, fr_value_box_t *out, fr_value_box_list_t *list, fr_type_t type, fr_value_box_list_action_t proc_action, bool flatten, size_t max_size)
Concatenate a list of value boxes.
#define fr_box_strvalue_buffer(_val)
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
static size_t char ** out