📄 dict.c
字号:
/* * dict.c Routines to read the dictionary file. * * Version: $Id: dict.c,v 1.50.2.4.2.18 2007/04/07 21:41:38 aland Exp $ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * Copyright 2000 The FreeRADIUS server project */static const char rcsid[] = "$Id: dict.c,v 1.50.2.4.2.18 2007/04/07 21:41:38 aland Exp $";#include "autoconf.h"#include <stdlib.h>#include <ctype.h>#include <string.h>#ifdef HAVE_MALLOC_H#include <malloc.h>#endif#ifdef HAVE_SYS_STAT_H#include <sys/stat.h>#endif#include <unistd.h>#include "missing.h"#include "libradius.h"#define DICT_VALUE_MAX_NAME_LEN (128)#define DICT_VENDOR_MAX_NAME_LEN (128)static lrad_hash_table_t *vendors_byname = NULL;static lrad_hash_table_t *vendors_byvalue = NULL;static lrad_hash_table_t *attributes_byname = NULL;static lrad_hash_table_t *attributes_byvalue = NULL;static lrad_hash_table_t *values_byvalue = NULL;static lrad_hash_table_t *values_byname = NULL;/* * For faster HUP's, we cache the stat information for * files we've $INCLUDEd */typedef struct dict_stat_t { struct dict_stat_t *next; char *name; time_t mtime;} dict_stat_t;static char *stat_root_dir = NULL;static char *stat_root_file = NULL;static dict_stat_t *stat_head = NULL;static dict_stat_t *stat_tail = NULL;typedef struct value_fixup_t { char attrstr[40]; DICT_VALUE *dval; struct value_fixup_t *next;} value_fixup_t;/* * So VALUEs in the dictionary can have forward references. */static value_fixup_t *value_fixup = NULL;static const LRAD_NAME_NUMBER type_table[] = { { "string", PW_TYPE_STRING }, { "integer", PW_TYPE_INTEGER }, { "ipaddr", PW_TYPE_IPADDR }, { "date", PW_TYPE_DATE }, { "abinary", PW_TYPE_ABINARY }, { "octets", PW_TYPE_OCTETS }, { "ifid", PW_TYPE_IFID }, { "ipv6addr", PW_TYPE_IPV6ADDR }, { "ipv6prefix", PW_TYPE_IPV6PREFIX }, { NULL, 0 }};/* * Create the hash of the name. * * We copy the hash function here because it's substantially faster. */#define FNV_MAGIC_INIT (0x811c9dc5)#define FNV_MAGIC_PRIME (0x01000193)static uint32_t dict_hashname(const char *name){ uint32_t hash = FNV_MAGIC_INIT; const char *p; for (p = name; *p != '\0'; p++) { int c = *(const unsigned char *) p; if (isalpha(c)) c = tolower(c); hash *= FNV_MAGIC_PRIME; hash ^= (uint32_t ) (c & 0xff); } return hash;}/* * Hash callback functions. */static uint32_t dict_attr_name_hash(const void *data){ return dict_hashname(((const DICT_ATTR *)data)->name);}static int dict_attr_name_cmp(const void *one, const void *two){ const DICT_ATTR *a = one; const DICT_ATTR *b = two; return strcasecmp(a->name, b->name);}static uint32_t dict_attr_value_hash(const void *data){ return lrad_hash(&((const DICT_ATTR *)data)->attr, sizeof(((const DICT_ATTR *)data)->attr));}static int dict_attr_value_cmp(const void *one, const void *two){ const DICT_ATTR *a = one; const DICT_ATTR *b = two; return a->attr - b->attr;}static uint32_t dict_vendor_name_hash(const void *data){ return dict_hashname(((const DICT_VENDOR *)data)->name);}static int dict_vendor_name_cmp(const void *one, const void *two){ const DICT_VENDOR *a = one; const DICT_VENDOR *b = two; return strcasecmp(a->name, b->name);}static uint32_t dict_vendor_value_hash(const void *data){ return lrad_hash(&(((const DICT_VENDOR *)data)->vendorpec), sizeof(((const DICT_VENDOR *)data)->vendorpec));}static int dict_vendor_value_cmp(const void *one, const void *two){ const DICT_VENDOR *a = one; const DICT_VENDOR *b = two; return a->vendorpec - b->vendorpec;}static uint32_t dict_value_name_hash(const void *data){ uint32_t hash; const DICT_VALUE *dval = data; hash = dict_hashname(dval->name); return lrad_hash_update(&dval->attr, sizeof(dval->attr), hash);}static int dict_value_name_cmp(const void *one, const void *two){ int rcode; const DICT_VALUE *a = one; const DICT_VALUE *b = two; rcode = a->attr - b->attr; if (rcode != 0) return rcode; return strcasecmp(a->name, b->name);}static uint32_t dict_value_value_hash(const void *data){ uint32_t hash; const DICT_VALUE *dval = data; hash = lrad_hash(&dval->attr, sizeof(dval->attr)); return lrad_hash_update(&dval->value, sizeof(dval->value), hash);}static int dict_value_value_cmp(const void *one, const void *two){ int rcode; const DICT_VALUE *a = one; const DICT_VALUE *b = two; rcode = a->attr - b->attr; if (rcode != 0) return rcode; return a->value - b->value;}/* * Free the list of stat buffers */static void dict_stat_free(void){ dict_stat_t *this, *next; free(stat_root_dir); stat_root_dir = NULL; free(stat_root_file); stat_root_file = NULL; if (!stat_head) { stat_tail = NULL; return; } for (this = stat_head; this != NULL; this = next) { next = this->next; free(this->name); free(this); } stat_head = stat_tail = NULL;}/* * Add an entry to the list of stat buffers. */static void dict_stat_add(const char *name, const struct stat *stat_buf){ dict_stat_t *this; this = malloc(sizeof(*this)); if (!this) return; memset(this, 0, sizeof(*this)); this->name = strdup(name); this->mtime = stat_buf->st_mtime; if (!stat_head) { stat_head = stat_tail = this; } else { stat_tail->next = this; stat_tail = this; }}/* * See if any dictionaries have changed. If not, don't * do anything. */static int dict_stat_check(const char *root_dir, const char *root_file){ struct stat buf; dict_stat_t *this; if (!stat_root_dir) return 0; if (!stat_root_file) return 0; if (strcmp(root_dir, stat_root_dir) != 0) return 0; if (strcmp(root_file, stat_root_file) != 0) return 0; if (!stat_head) return 0; /* changed, reload */ for (this = stat_head; this != NULL; this = this->next) { if (stat(this->name, &buf) < 0) return 0; if (buf.st_mtime != this->mtime) return 0; } return 1;}/* * Free the dictionary_attributes and dictionary_values lists. */void dict_free(void){ /* * Free the tables */ lrad_hash_table_free(vendors_byname); lrad_hash_table_free(vendors_byvalue); vendors_byname = NULL; vendors_byvalue = NULL; lrad_hash_table_free(attributes_byname); lrad_hash_table_free(attributes_byvalue); attributes_byname = NULL; attributes_byvalue = NULL; lrad_hash_table_free(values_byname); lrad_hash_table_free(values_byvalue); values_byname = NULL; values_byvalue = NULL; dict_stat_free();}/* * Add vendor to the list. */int dict_addvendor(const char *name, int value){ size_t length; DICT_VENDOR *dv; if (value >= (1 << 16)) { librad_log("dict_addvendor: Cannot handle vendor ID larger than 65535"); return -1; } if ((length = strlen(name)) >= DICT_VENDOR_MAX_NAME_LEN) { librad_log("dict_addvendor: vendor name too long"); return -1; } if ((dv = malloc(sizeof(*dv) + length)) == NULL) { librad_log("dict_addvendor: out of memory"); return -1; } strcpy(dv->name, name); dv->vendorpec = value; dv->type = dv->length = 1; /* defaults */ if (!lrad_hash_table_insert(vendors_byname, dv)) { DICT_VENDOR *old_dv; old_dv = lrad_hash_table_finddata(vendors_byname, dv); if (!old_dv) { librad_log("dict_addvendor: Failed inserting vendor name %s", name); return -1; } if (old_dv->vendorpec != dv->vendorpec) { librad_log("dict_addvendor: Duplicate vendor name %s", name); return -1; } /* * Already inserted. Discard the duplicate entry. */ free(dv); return 0; } /* * Insert the SAME pointer (not free'd when this table is * deleted), into another table. * * We want this behaviour because we want OLD names for * the attributes to be read from the configuration * files, but when we're printing them, (and looking up * by value) we want to use the NEW name. */ if (!lrad_hash_table_replace(vendors_byvalue, dv)) { librad_log("dict_addvendor: Failed inserting vendor %s", name); return -1; } return 0;}/* * Add an attribute to the dictionary. */int dict_addattr(const char *name, int vendor, int type, int value, ATTR_FLAGS flags){ static int max_attr = 0; DICT_ATTR *attr; if (strlen(name) > (sizeof(attr->name) -1)) { librad_log("dict_addattr: attribute name too long"); return -1; } /* * If the value is '-1', that means use a pre-existing * one (if it already exists). If one does NOT already exist, * then create a new attribute, with a non-conflicting value, * and use that. */ if (value == -1) { if (dict_attrbyname(name)) { return 0; /* exists, don't add it again */ } value = ++max_attr; } else if (vendor == 0) { /* * Update 'max_attr' */ if (value > max_attr) { max_attr = value; } } if (value < 0) { librad_log("dict_addattr: ATTRIBUTE has invalid number (less than zero)"); return -1; } if (value >= 65536) { librad_log("dict_addattr: ATTRIBUTE has invalid number (larger than 65535)."); return -1; } if (vendor) { DICT_VENDOR *dv = dict_vendorbyvalue(vendor); /* * If the vendor isn't defined, die/ */ if (!dv) { librad_log("dict_addattr: Unknown vendor"); return -1; } /* * With a few exceptions, attributes can only be * 1..255. The check above catches the less than * zero case. */ if ((dv->type == 1) && (value >= 256)) { librad_log("dict_addattr: ATTRIBUTE has invalid number (larger than 255)."); return -1; } /* else 256..65535 are allowed */ } /* * Create a new attribute for the list */ if ((attr = malloc(sizeof(*attr))) == NULL) { librad_log("dict_addattr: out of memory"); return -1; } strcpy(attr->name, name); attr->attr = value; attr->attr |= (vendor << 16); /* FIXME: hack */ attr->type = type; attr->flags = flags; attr->vendor = vendor; /* * Insert the attribute, only if it's not a duplicate. */ if (!lrad_hash_table_insert(attributes_byname, attr)) { DICT_ATTR *a; /* * If the attribute has identical number, then * ignore the duplicate. */ a = lrad_hash_table_finddata(attributes_byname, attr); if (a && (strcasecmp(a->name, attr->name) == 0)) { if (a->attr != attr->attr) { librad_log("dict_addattr: Duplicate attribute name %s", name); free(attr); return -1; } /* * Same name, same vendor, same attr, * maybe the flags and/or type is * different. Let the new value * over-ride the old one. */ } lrad_hash_table_delete(attributes_byvalue, a); if (!lrad_hash_table_replace(attributes_byname, attr)) { librad_log("dict_addattr: Internal error storing attribute %s", name); free(attr); return -1; } } /* * Insert the SAME pointer (not free'd when this entry is * deleted), into another table. * * We want this behaviour because we want OLD names for * the attributes to be read from the configuration * files, but when we're printing them, (and looking up * by value) we want to use the NEW name. */ if (!lrad_hash_table_replace(attributes_byvalue, attr)) { librad_log("dict_addattr: Failed inserting attribute name %s", name); return -1; } return 0;}/* * Add a value for an attribute to the dictionary. */int dict_addvalue(const char *namestr, const char *attrstr, int value){ size_t length; DICT_ATTR *dattr; DICT_VALUE *dval; if ((length = strlen(namestr)) >= DICT_VALUE_MAX_NAME_LEN) { librad_log("dict_addvalue: value name too long"); return -1; } if ((dval = malloc(sizeof(*dval) + length)) == NULL) { librad_log("dict_addvalue: out of memory"); return -1; } memset(dval, 0, sizeof(*dval)); strcpy(dval->name, namestr); dval->value = value; /* * Remember which attribute is associated with this * value, if possible. */ dattr = dict_attrbyname(attrstr); if (dattr) { dval->attr = dattr->attr; /* * Octets is allowed as a work-around for the * fact that branch_1_1 doesn't have BYTE or SHORT */ if ((dattr->type != PW_TYPE_INTEGER) && (dattr->type != PW_TYPE_OCTETS)) { free(dval); librad_log("dict_addvalue: VALUEs can only be defined for'integer' types"); return -1; } } else { value_fixup_t *fixup; fixup = (value_fixup_t *) malloc(sizeof(*fixup)); if (!fixup) { free(dval); librad_log("dict_addvalue: out of memory"); return -1; } memset(fixup, 0, sizeof(*fixup)); strNcpy(fixup->attrstr, attrstr, sizeof(fixup->attrstr)); fixup->dval = dval; /* * Insert to the head of the list. */ fixup->next = value_fixup; value_fixup = fixup; return 0; } /* * Add the value into the dictionary. */ if (!lrad_hash_table_insert(values_byname, dval)) { if (dattr) { DICT_VALUE *old; /* * Suppress duplicates with the same * name and value. There are lots in * dictionary.ascend. */ old = dict_valbyname(dattr->attr, namestr); if (old && (old->value == dval->value)) { free(dval); return 0; } } free(dval); librad_log("dict_addvalue: Duplicate value name %s for attribute %s", namestr, attrstr); return -1; } /* * There are multiple VALUE's, keyed by attribute, so we * take care of that here. */ if (!lrad_hash_table_replace(values_byvalue, dval)) { librad_log("dict_addvalue: Failed inserting value %s", namestr); return -1; } return 0;}/* * Process the ATTRIBUTE command */static int process_attribute(const char* fn, const int line, const int block_vendor, char **argv, int argc){ int vendor = 0; int value; int type; char *s, *c; ATTR_FLAGS flags; if ((argc < 3) || (argc > 4)) { librad_log("dict_init: %s[%d]: invalid ATTRIBUTE line", fn, line); return -1; } /* * Validate all entries */ if (!isdigit((int) argv[1][0])) { librad_log("dict_init: %s[%d]: invalid value", fn, line); return -1; } sscanf(argv[1], "%i", &value); /* * find the type of the attribute. */ type = lrad_str2int(type_table, argv[2], -1); if (type < 0) { librad_log("dict_init: %s[%d]: invalid type \"%s\"", fn, line, argv[2]); return -1; } /* * Only look up the vendor if the string * is non-empty. */ memset(&flags, 0, sizeof(flags)); if (argc == 4) { s = strtok(argv[3], ","); while (s) { if (strcmp(s, "has_tag") == 0 || strcmp(s, "has_tag=1") == 0) { /* Boolean flag, means this is a tagged attribute */ flags.has_tag = 1; } else if (strncmp(s, "encrypt=", 8) == 0) { /* Encryption method, defaults to 0 (none). Currently valid is just type 2, Tunnel-Password style, which can only be applied to strings. */ flags.encrypt = strtol(s + 8, &c, 0); if (*c) { librad_log( "dict_init: %s[%d] invalid option %s", fn, line, s); return -1; } } else { /* Must be a vendor 'flag'... */ if (strncmp(s, "vendor=", 7) == 0) { /* New format */ s += 7; } vendor = dict_vendorbyname(s); if (!vendor) { librad_log( "dict_init: %s[%d]: unknown vendor %s", fn, line, s); return -1; } if (block_vendor && argv[3][0] &&
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -