* attr && value are allocated contiguous with cp.
*/
+ free((*cp)->item.filename);
#ifndef NDEBUG
- memset(*cp, 0, sizeof(*cp));
+ memset(*cp, 0, sizeof(cp));
#endif
free(*cp);
((*cd)->free)((*cd)->data);
}
#ifndef NDEBUG
- memset(*cd, 0, sizeof(*cd));
+ memset(*cd, 0, sizeof(cd));
#endif
free(*cd);
*cd = NULL;
rad_assert(strcmp(one->name1, two->name1) == 0);
if (!one->name2 && !two->name2) return 0;
- if (!one->name2) return -1;
- if (!two->name2) return +1;
+ if (one->name2 && !two->name2) return -1;
+ if (!one->name2 && two->name2) return +1;
return strcmp(one->name2, two->name2);
}
* Free up dynamically allocated string pointers.
*/
for (i = 0; variables[i].name != NULL; i++) {
+ int type;
char **p;
- if ((variables[i].type != PW_TYPE_STRING_PTR) &&
- (variables[i].type != PW_TYPE_FILENAME)) {
+ type = variables[i].type;
+
+ if (type == PW_TYPE_SUBSECTION) {
+ CONF_SECTION *subcs;
+ subcs = cf_section_sub_find(cs, variables[i].name);
+
+ if (!subcs) continue;
+
+ if (!variables[i].dflt) continue;
+
+ cf_section_parse_free(subcs, base);
+ continue;
+ }
+
+ if ((type != PW_TYPE_STRING_PTR) &&
+ (type != PW_TYPE_FILENAME)) {
continue;
}
free(*p);
*p = NULL;
}
+
+ cs->variables = NULL;
}
/*
* And free the section
*/
+ free((*cs)->item.filename);
#ifndef NDEBUG
- memset(*cs, 0, sizeof(*cs));
+ memset(*cs, 0, sizeof(cs));
#endif
free(*cs);
return cs;
}
+/*
+ * Replace pair in a given section with a new pair,
+ * of the given value.
+ */
+int cf_pair_replace(CONF_SECTION *cs, CONF_PAIR *cp, const char *value)
+{
+ CONF_PAIR *newp;
+ CONF_ITEM *ci, *cn, **last;
+
+ newp = cf_pair_alloc(cp->attr, value, cp->operator, cp->value_type,
+ cs);
+ if (!newp) return -1;
+
+ ci = cf_pairtoitem(cp);
+ cn = cf_pairtoitem(newp);
+
+ /*
+ * Find the old one from the linked list, and replace it
+ * with the new one.
+ */
+ for (last = &cs->children; (*last) != NULL; last = &(*last)->next) {
+ if (*last == ci) {
+ cn->next = (*last)->next;
+ *last = cn;
+ ci->next = NULL;
+ break;
+ }
+ }
+
+ rbtree_deletebydata(cs->pair_tree, ci);
+
+ rbtree_insert(cs->pair_tree, cn);
+
+ return 0;
+}
+
/*
* Add an item to a configuration section.
*/
static void cf_item_add(CONF_SECTION *cs, CONF_ITEM *ci)
{
+ if (!cs || !ci) return;
+
if (!cs->children) {
rad_assert(cs->tail == NULL);
cs->children = ci;
if (!cs->section_tree) {
cs->section_tree = rbtree_create(section_cmp, NULL, 0);
- /* ignore any errors */
+ if (!cs->section_tree) {
+ radlog(L_ERR, "Out of memory");
+ _exit(1);
+ }
}
- if (cs->section_tree) {
- rbtree_insert(cs->section_tree, cs_new); }
+ rbtree_insert(cs->section_tree, cs_new);
/*
* Two names: find the named instance.
char name[8192];
char *p;
+ if (cs == NULL)
+ goto no_such_item;
+
strlcpy(name, ptr, sizeof(name));
p = name;
* "foo.bar.baz" means "from the root"
*/
} else if (strchr(p, '.') != NULL) {
+ if (!parentcs) goto no_such_item;
+
cs = parentcs;
}
/*
* "foo" is "in the current section, OR in main".
*/
- if ((p == name) && (cs != parentcs)) {
+ if ((p == name) && (parentcs != NULL) && (cs != parentcs)) {
cs = parentcs;
goto retry;
}
CONF_SECTION *cf_top_section(CONF_SECTION *cs)
{
+ if (!cs) return NULL;
+
while (cs->item.parent != NULL) {
cs = cs->item.parent;
}
*/
static const char *cf_expand_variables(const char *cf, int *lineno,
CONF_SECTION *outercs,
- char *output, const char *input)
+ char *output, size_t outsize,
+ const char *input)
{
char *p;
const char *end, *ptr;
* Substitute the value of the variable.
*/
cp = cf_itemtopair(ci);
+ if (!cp->value) {
+ radlog(L_ERR, "%s[%d]: Reference \"%s\" has no value",
+ cf, *lineno, input);
+ return NULL;
+ }
+
+ if (p + strlen(cp->value) >= output + outsize) {
+ radlog(L_ERR, "%s[%d]: Reference \"%s\" is too long",
+ cf, *lineno, input);
+ return NULL;
+ }
+
strcpy(p, cp->value);
p += strlen(p);
ptr = end + 1;
- } else if (memcmp(ptr, "$ENV{", 5) == 0) {
+ } else if (strncmp(ptr, "$ENV{", 5) == 0) {
char *env;
ptr += 5;
env = name;
}
+ if (p + strlen(env) >= output + outsize) {
+ radlog(L_ERR, "%s[%d]: Reference \"%s\" is too long",
+ cf, *lineno, input);
+ return NULL;
+ }
+
strcpy(p, env);
p += strlen(p);
ptr = end + 1;
*/
*(p++) = *(ptr++);
}
+
+ if (p >= (output + outsize)) {
+ radlog(L_ERR, "%s[%d]: Reference \"%s\" is too long",
+ cf, *lineno, input);
+ return NULL;
+ }
} /* loop over all of the input string. */
*p = '\0';
return output;
}
+static const char *parse_spaces = " ";
+
/*
* Parses an item (not a CONF_ITEM) into the specified format,
char **q;
const char *value;
fr_ipaddr_t ipaddr;
- const CONF_PAIR *cp;
+ const CONF_PAIR *cp = NULL;
+ int depth;
char ipbuf[128];
- cp = cf_pair_find(cs, name);
+ if (cs) {
+ depth = cs->depth;
+ cp = cf_pair_find(cs, name);
+ } else {
+ depth = 0;
+ }
+
if (cp) {
value = cp->value;
radlog(L_ERR, "Bad value \"%s\" for boolean variable %s", value, name);
return -1;
}
- cf_log_info(cs, "\t%s = %s", name, value);
+ cf_log_info(cs, "%.*s\t%s = %s",
+ depth, parse_spaces, name, value);
break;
case PW_TYPE_INTEGER:
*(int *)data = strtol(value, 0, 0);
- cf_log_info(cs, "\t%s = %d", name, *(int *)data);
+ cf_log_info(cs, "%.*s\t%s = %d",
+ depth, parse_spaces, name, *(int *)data);
break;
case PW_TYPE_STRING_PTR:
if (value == dflt) {
char buffer[8192];
- int lineno = cs->item.lineno;
+ int lineno = 0;
+
+ if (cs) lineno = cs->item.lineno;
/*
* FIXME: sizeof(buffer)?
*/
- value = cf_expand_variables("?",
+ value = cf_expand_variables("<internal>",
&lineno,
- cs, buffer, value);
- if (!value) return -1;
+ cs, buffer, sizeof(buffer),
+ value);
+ if (!value) {
+ cf_log_err(cf_sectiontoitem(cs),"Failed expanding variable %s", name);
+ return -1;
+ }
}
- cf_log_info(cs, "\t%s = \"%s\"", name, value ? value : "(null)");
+ cf_log_info(cs, "%.*s\t%s = \"%s\"",
+ depth, parse_spaces, name, value ? value : "(null)");
*q = value ? strdup(value) : NULL;
break;
* expanded automagically when the configuration
* file was read.
*/
- if (value == dflt) {
+ if ((value == dflt) && cs) {
char buffer[8192];
- int lineno = cs->item.lineno;
+ int lineno = 0;
/*
* FIXME: sizeof(buffer)?
*/
value = cf_expand_variables("?",
&lineno,
- cs, buffer, value);
+ cs, buffer, sizeof(buffer),
+ value);
if (!value) return -1;
}
- cf_log_info(cs, "\t%s = \"%s\"", name, value ? value : "(null)");
+ cf_log_info(cs, "%.*s\t%s = \"%s\"",
+ depth, parse_spaces, name, value ? value : "(null)");
*q = value ? strdup(value) : NULL;
/*
* And now we "stat" the file.
+ *
+ * FIXME: This appears to leak memory on exit,
+ * and we don't use this information. So it's
+ * commented out for now.
*/
- if (*q) {
+ if (0 && *q) {
struct stat buf;
if (stat(*q, &buf) == 0) {
*/
if (strcmp(value, "*") == 0) {
*(uint32_t *) data = htonl(INADDR_ANY);
- cf_log_info(cs, "\t%s = *", name);
+ cf_log_info(cs, "%.*s\t%s = *",
+ depth, parse_spaces, name);
break;
}
if (ip_hton(value, AF_INET, &ipaddr) < 0) {
}
if (strspn(value, "0123456789.") == strlen(value)) {
- cf_log_info(cs, "\t%s = %s", name, value);
+ cf_log_info(cs, "%.*s\t%s = %s",
+ depth, parse_spaces, name, value);
} else {
- cf_log_info(cs, "\t%s = %s IP address [%s]", name, value,
+ cf_log_info(cs, "%.*s\t%s = %s IP address [%s]",
+ depth, parse_spaces, name, value,
ip_ntoh(&ipaddr, ipbuf, sizeof(ipbuf)));
}
*(uint32_t *) data = ipaddr.ipaddr.ip4addr.s_addr;
radlog(L_ERR, "Can't find IPv6 address for host %s", value);
return -1;
}
- cf_log_info(cs, "\t%s = %s IPv6 address [%s]", name, value,
- ip_ntoh(&ipaddr, ipbuf, sizeof(ipbuf)));
+ cf_log_info(cs, "%.*s\t%s = %s IPv6 address [%s]",
+ depth, parse_spaces, name, value,
+ ip_ntoh(&ipaddr, ipbuf, sizeof(ipbuf)));
memcpy(data, &ipaddr.ipaddr.ip6addr,
sizeof(ipaddr.ipaddr.ip6addr));
break;
default:
radlog(L_ERR, "type %d not supported yet", type);
return -1;
- break;
} /* switch over variable type */
+ if (!cp) {
+ CONF_PAIR *cpn;
+
+ cpn = cf_pair_alloc(name, value, T_OP_SET, T_BARE_WORD, cs);
+ if (!cpn) return -1;
+ cpn->item.filename = strdup("<internal>");
+ cpn->item.lineno = 0;
+ cf_item_add(cs, cf_pairtoitem(cpn));
+ }
+
return rcode;
}
-static const char *parse_spaces = " ";
+
+/*
+ * A copy of cf_section_parse that initializes pointers before
+ * parsing them.
+ */
+static void cf_section_parse_init(CONF_SECTION *cs, void *base,
+ const CONF_PARSER *variables)
+{
+ int i;
+ void *data;
+
+ for (i = 0; variables[i].name != NULL; i++) {
+ if (variables[i].type == PW_TYPE_SUBSECTION) {
+ CONF_SECTION *subcs;
+ subcs = cf_section_sub_find(cs, variables[i].name);
+ if (!subcs) continue;
+
+ if (!variables[i].dflt) continue;
+
+ cf_section_parse_init(subcs, base,
+ (const CONF_PARSER *) variables[i].dflt);
+ continue;
+ }
+
+ if ((variables[i].type != PW_TYPE_STRING_PTR) &&
+ (variables[i].type != PW_TYPE_FILENAME)) {
+ continue;
+ }
+
+ if (variables[i].data) {
+ data = variables[i].data; /* prefer this. */
+ } else if (base) {
+ data = ((char *)base) + variables[i].offset;
+ } else {
+ continue;
+ }
+
+ *(char **) data = NULL;
+ } /* for all variables in the configuration section */
+}
/*
* Parse a configuration section into user-supplied variables.
cs->name1, cs->name2);
}
+ cf_section_parse_init(cs, base, variables);
+
/*
* Handle the known configuration parameters.
*/
* Parse error.
*/
if (*q != '{') {
+ DEBUG2("Expected open brace '{' after condition at %s", p);
return 0;
}
}
}
+ DEBUG3("Unexpected error");
return 0;
}
+int cf_exclude_file(const char *filename)
+{
+ int i;
+ size_t len;
+ const char *p = filename;
+
+ /*
+ * FIXME: Maybe later make this a globally set configuration
+ * variable. But that's low priority.
+ */
+ static const char *excluded[] = {
+ "rpmsave", "rpmnew", "dpkg-new", "dpkg-dist", "dpkg-old",
+ "bak", NULL
+ };
+
+ if (!p || !*p) return TRUE; /* coding error */
+
+ if (*p == '.') return TRUE; /* ".", "..", ".foo", ... */
+
+ if (*p == '#') return TRUE; /* #foo# */
+
+ len = strlen(p);
+ if (p[len - 1] == '~') return TRUE; /* foo~ */
+
+ p = strrchr(p, '.');
+ if (!p) return FALSE; /* just "foo", it's OK */
+
+ p++;
+ for (i = 0; excluded[i] != NULL; i++) {
+ if (strcmp(p, excluded[i]) == 0) return TRUE;
+ }
+
+ return FALSE;
+}
+
static const char *cf_local_file(CONF_SECTION *cs, const char *local,
char *buffer, size_t bufsize)
return buffer;
}
+static int seen_too_much(const char *filename, int lineno, const char *ptr)
+{
+ while (*ptr) {
+ if (isspace(*ptr)) {
+ ptr++;
+ continue;
+ }
+
+ if (*ptr == '#') return FALSE;
+
+ break;
+ }
+
+ if (*ptr) {
+ radlog(L_ERR, "%s[%d] Unexpected text %s. See \"man unlang\"",
+ filename, lineno, ptr);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
/*
* Read a part of the config file.
* Read, checking for line continuations ('\\' at EOL)
*/
for (;;) {
- int eof;
+ int at_eof;
/*
* Get data, and remember if we are at EOF.
*/
- eof = (fgets(cbuf, sizeof(buf) - (cbuf - buf), fp) == NULL);
+ at_eof = (fgets(cbuf, sizeof(buf) - (cbuf - buf), fp) == NULL);
(*lineno)++;
/*
* conditions.
*/
if (cbuf == buf) {
- if (eof) break;
+ if (at_eof) break;
ptr = buf;
while (*ptr && isspace((int) *ptr)) ptr++;
if (!*ptr || (*ptr == '#')) continue;
- } else if (eof || (len == 0)) {
+ } else if (at_eof || (len == 0)) {
radlog(L_ERR, "%s[%d]: Continuation at EOF is illegal",
filename, *lineno);
return -1;
}
ptr = cbuf = buf;
- t1 = gettoken(&ptr, buf1, sizeof(buf1));
+
+ /*
+ * The parser is getting to be evil.
+ */
+ while ((*ptr == ' ') || (*ptr == '\t')) ptr++;
+
+ if (((ptr[0] == '%') && (ptr[1] == '{')) ||
+ (ptr[0] == '`')) {
+ int hack;
+
+ if (ptr[0] == '%') {
+ hack = rad_copy_variable(buf1, ptr);
+ } else {
+ hack = rad_copy_string(buf1, ptr);
+ }
+ if (hack < 0) {
+ radlog(L_ERR, "%s[%d]: Invalid expansion: %s",
+ filename, *lineno, ptr);
+ return -1;
+ }
+
+ t1 = T_BARE_WORD;
+ ptr += hack;
+
+ t2 = gettoken(&ptr, buf2, sizeof(buf2));
+ switch (t2) {
+ case T_EOL:
+ case T_HASH:
+ goto do_bare_word;
+
+ default:
+ radlog(L_ERR, "%s[%d]: Invalid expansion: %s",
+ filename, *lineno, ptr);
+ return -1;
+ }
+ } else {
+ t1 = gettoken(&ptr, buf1, sizeof(buf1));
+ }
/*
* The caller eats "name1 name2 {", and calls us
}
this = this->item.parent;
+ if (seen_too_much(filename, *lineno, ptr)) return -1;
continue;
}
if (buf2[0] == '$') relative = 0;
- value = cf_expand_variables(filename, lineno, this, buf, buf2);
+ value = cf_expand_variables(filename, lineno, this, buf, sizeof(buf), buf2);
if (!value) return -1;
if (!FR_DIR_IS_RELATIVE(value)) relative = 0;
struct stat stat_buf;
DEBUG2("including files in directory %s", value );
+#ifdef S_IWOTH
+ /*
+ * Security checks.
+ */
+ if (stat(value, &stat_buf) < 0) {
+ radlog(L_ERR, "%s[%d]: Failed reading directory %s: %s",
+ filename, *lineno,
+ value, strerror(errno));
+ return -1;
+ }
+
+ if ((stat_buf.st_mode & S_IWOTH) != 0) {
+ radlog(L_ERR|L_CONS, "%s[%d]: Directory %s is globally writable. Refusing to start due to insecure configuration.",
+ filename, *lineno, value);
+ return -1;
+ }
+#endif
dir = opendir(value);
if (!dir) {
radlog(L_ERR, "%s[%d]: Error reading directory %s: %s",
}
/*
- * Read the directory, ignoring "." files.
+ * Read the directory, ignoring some files.
*/
while ((dp = readdir(dir)) != NULL) {
- const char *p;
-
- if (dp->d_name[0] == '.') continue;
-
- /*
- * Check for valid characters
- */
- for (p = dp->d_name; *p != '\0'; p++) {
- if (isalpha((int)*p) ||
- isdigit((int)*p) ||
- (*p == '-') ||
- (*p == '_') ||
- (*p == '.')) continue;
- break;
- }
- if (*p != '\0') continue;
+ if (cf_exclude_file(dp->d_name))
+ continue;
snprintf(buf2, sizeof(buf2), "%s%s",
value, dp->d_name);
if (strcasecmp(buf1, "$template") == 0) {
CONF_ITEM *ci;
- CONF_SECTION *parentcs;
+ CONF_SECTION *parentcs, *templatecs;
t2 = getword(&ptr, buf2, sizeof(buf2));
parentcs = cf_top_section(current);
- ci = cf_reference_item(parentcs, this, buf2);
+ templatecs = cf_section_sub_find(parentcs, "templates");
+ if (!templatecs) {
+ radlog(L_ERR, "%s[%d]: No \"templates\" section for reference \"%s\"",
+ filename, *lineno, buf2);
+ return -1;
+ }
+
+ ci = cf_reference_item(parentcs, templatecs, buf2);
if (!ci || (ci->type != CONF_ITEM_SECTION)) {
radlog(L_ERR, "%s[%d]: Reference \"%s\" not found",
filename, *lineno, buf2);
switch (t2) {
case T_EOL:
case T_HASH:
+ do_bare_word:
+ t3 = t2;
t2 = T_OP_EQ;
value = NULL;
goto do_set;
case T_OP_SUB:
case T_OP_LE:
case T_OP_GE:
+ case T_OP_CMP_FALSE:
if (!this || (strcmp(this->name1, "update") != 0)) {
radlog(L_ERR, "%s[%d]: Invalid operator in assignment",
filename, *lineno);
return -1;
}
+ /* FALL-THROUGH */
case T_OP_EQ:
case T_OP_SET:
- do_set:
t3 = getstring(&ptr, buf3, sizeof(buf3));
+ if (t3 == T_OP_INVALID) {
+ radlog(L_ERR, "%s[%d]: Parse error: %s",
+ filename, *lineno,
+ fr_strerror());
+ return -1;
+ }
+
+ /*
+ * These are not allowed. Print a
+ * helpful error message.
+ */
+ if ((t3 == T_BACK_QUOTED_STRING) &&
+ (!this || (strcmp(this->name1, "update") != 0))) {
+ radlog(L_ERR, "%s[%d]: Syntax error: Invalid string `...` in assignment",
+ filename, *lineno);
+ return -1;
+ }
/*
* Handle variable substitution via ${foo}
if ((t3 == T_BARE_WORD) ||
(t3 == T_DOUBLE_QUOTED_STRING)) {
value = cf_expand_variables(filename, lineno, this,
- buf, buf3);
+ buf, sizeof(buf), buf3);
if (!value) return -1;
} else if ((t3 == T_EOL) ||
(t3 == T_HASH)) {
/*
* Add this CONF_PAIR to our CONF_SECTION
*/
+ do_set:
cpn = cf_pair_alloc(buf1, value, t2, t3, this);
- cpn->item.filename = filename;
+ cpn->item.filename = strdup(filename);
cpn->item.lineno = *lineno;
cf_item_add(this, cf_pairtoitem(cpn));
continue;
buf2[0] = '(';
memcpy(buf2 + 1, ptr, end - ptr);
buf2[end - ptr + 1] = '\0';
- ptr = end + 1;
+ ptr = end;
t2 = T_BARE_WORD;
+
+ if (gettoken(&ptr, buf3, sizeof(buf3)) != T_LCBRACE) {
+ radlog(L_ERR, "%s[%d]: Expected '{'",
+ filename, *lineno);
+ return -1;
+ }
goto section_alloc;
} else {
filename, *lineno, buf1, buf2);
return -1;
}
+ /* FALL-THROUGH */
case T_LCBRACE:
section_alloc:
+ if (seen_too_much(filename, *lineno, ptr)) return -1;
+
css = cf_section_alloc(buf1,
t2 == T_LCBRACE ? NULL : buf2,
this);
return -1;
}
cf_item_add(this, cf_sectiontoitem(css));
- css->item.filename = filename;
+ css->item.filename = strdup(filename);
css->item.lineno = *lineno;
/*
DEBUG2( "including configuration file %s", filename);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ radlog(L_ERR|L_CONS, "Unable to open file \"%s\": %s",
+ filename, strerror(errno));
+ return -1;
+ }
+
if (stat(filename, &statbuf) == 0) {
#ifdef S_IWOTH
if ((statbuf.st_mode & S_IWOTH) != 0) {
+ fclose(fp);
radlog(L_ERR|L_CONS, "Configuration file %s is globally writable. Refusing to start due to insecure configuration.",
filename);
return -1;
#ifdef S_IROTH
if (0 && (statbuf.st_mode & S_IROTH) != 0) {
+ fclose(fp);
radlog(L_ERR|L_CONS, "Configuration file %s is globally readable. Refusing to start due to insecure configuration.",
filename);
return -1;
#endif
}
- fp = fopen(filename, "r");
- if (!fp) {
- radlog(L_ERR|L_CONS, "Unable to open file \"%s\": %s",
- filename, strerror(errno));
+ if (cf_data_find_internal(cs, filename, PW_TYPE_FILENAME)) {
+ fclose(fp);
+ radlog(L_ERR, "Cannot include the same file twice: \"%s\"",
+ filename);
return -1;
}
if (cf_data_add_internal(cs, filename, mtime, free,
PW_TYPE_FILENAME) < 0) {
fclose(fp);
- radlog(L_ERR|L_CONS, "Internal error open file \"%s\"",
+ radlog(L_ERR|L_CONS, "Internal error opening file \"%s\"",
filename);
return -1;
}
cd = cf_data_find_internal(cs, filename, PW_TYPE_FILENAME);
if (!cd) {
fclose(fp);
- radlog(L_ERR|L_CONS, "Internal error open file \"%s\"",
+ radlog(L_ERR|L_CONS, "Internal error opening file \"%s\"",
filename);
return -1;
}
- if (!cs->item.filename) cs->item.filename = filename;
+ if (!cs->item.filename) cs->item.filename = strdup(filename);
/*
* Read the section. It's OK to have EOF without a
p = strrchr(cp->value, FR_DIR_SEP);
if (p) *p = '\0';
- cp->item.filename = "internal";
+ cp->item.filename = strdup("<internal>");
cp->item.lineno = 0;
cf_item_add(cs, cf_pairtoitem(cp));
return (pair ? pair->value : NULL);
}
+FR_TOKEN cf_pair_operator(CONF_PAIR *pair)
+{
+ return (pair ? pair->operator : T_OP_INVALID);
+}
+
/*
* Copied here for error reporting.
*/
-extern void librad_log(const char *, ...);
+extern void fr_strerror_printf(const char *, ...);
/*
* Turn a CONF_PAIR into a VALUE_PAIR
*/
VALUE_PAIR *cf_pairtovp(CONF_PAIR *pair)
{
- DICT_ATTR *da;
VALUE_PAIR *vp;
if (!pair) {
- librad_log("Internal error");
- return NULL;
- }
-
- da = dict_attrbyname(pair->attr);
- if (!da) {
- librad_log("Unknown attribute %s", pair->attr);
+ fr_strerror_printf("Internal error");
return NULL;
}
if (!pair->value) {
- librad_log("No value given for attribute %s", pair->attr);
+ fr_strerror_printf("No value given for attribute %s", pair->attr);
return NULL;
}
- vp = pairalloc(da);
+ /*
+ * pairmake handles tags. pairalloc() doesn't.
+ */
+ vp = pairmake(pair->attr, NULL, pair->operator);
if (!vp) {
- librad_log("Out of memory");
return NULL;
}
- vp->operator = pair->operator;
+ /*
+ * Ignore the value if it's a false comparison.
+ */
+ if (pair->operator == T_OP_CMP_FALSE) return vp;
- if ((pair->value_type == T_BARE_WORD) ||
- (pair->value_type == T_SINGLE_QUOTED_STRING)) {
+ if (pair->value_type == T_BARE_WORD) {
+ if ((vp->type == PW_TYPE_STRING) &&
+ (pair->value[0] == '0') && (pair->value[1] == 'x')) {
+ vp->type = PW_TYPE_OCTETS;
+ }
+ if (!pairparsevalue(vp, pair->value)) {
+ pairfree(&vp);
+ return NULL;
+ }
+ vp->flags.do_xlat = 0;
+
+ } else if (pair->value_type == T_SINGLE_QUOTED_STRING) {
if (!pairparsevalue(vp, pair->value)) {
pairfree(&vp);
return NULL;
return (cp ? cp->value : NULL);
}
+
+CONF_SECTION *cf_section_find_name2(const CONF_SECTION *section,
+ const char *name1, const char *name2)
+{
+ const char *their2;
+ CONF_ITEM *ci;
+
+ if (!section || !name1) return NULL;
+
+ for (ci = cf_sectiontoitem(section); ci; ci = ci->next) {
+ if (ci->type != CONF_ITEM_SECTION)
+ continue;
+
+ if (strcmp(cf_itemtosection(ci)->name1, name1) != 0)
+ continue;
+
+ their2 = cf_itemtosection(ci)->name2;
+
+ if ((!name2 && !their2) ||
+ (name2 && their2 && (strcmp(name2, their2) == 0))) {
+ return cf_itemtosection(ci);
+ }
+ }
+
+ return NULL;
+}
+
/*
* Return the next pair after a CONF_PAIR
* with a certain name (char *attr) If the requested
{
CONF_ITEM *ci;
+ if (!cs) return NULL;
+
/*
* If pair is NULL this must be a first time run
* Find the pair with correct name
{
CONF_ITEM *ci;
+ if (!name) return NULL; /* can't find an un-named section */
+
/*
* Do the fast lookup if possible.
*/
- if (name && cs->section_tree) {
+ if (cs->section_tree) {
CONF_SECTION mycs;
mycs.name1 = name;
CONF_ITEM *ci;
if (!cs) cs = mainconfig.config;
-
- if (name1 && (cs->section_tree)) {
+ if (!cs) return NULL;
+ if (name1) {
CONF_SECTION mycs, *master_cs;
+ if (!cs->section_tree) return NULL;
+
mycs.name1 = name1;
mycs.name2 = name2;
master_cs = rbtree_finddata(cs->section_tree, &mycs);
- if (master_cs) {
- return rbtree_finddata(master_cs->name2_tree, &mycs);
+ if (!master_cs) return NULL;
+
+ /*
+ * Look it up in the name2 tree. If it's there,
+ * return it.
+ */
+ if (master_cs->name2_tree) {
+ CONF_SECTION *subcs;
+
+ subcs = rbtree_finddata(master_cs->name2_tree, &mycs);
+ if (subcs) return subcs;
+ }
+
+ /*
+ * We don't insert ourselves into the name2 tree.
+ * So if there's nothing in the name2 tree, maybe
+ * *we* are the answer.
+ */
+ if (!master_cs->name2 && name2) return NULL;
+ if (master_cs->name2 && !name2) return NULL;
+ if (!master_cs->name2 && !name2) return master_cs;
+
+ if (strcmp(master_cs->name2, name2) == 0) {
+ return master_cs;
}
+
+ return NULL;
}
/*
continue; /* don't do the string comparisons below */
}
- if ((strcmp(subcs->name1, name1) == 0) &&
- (subcs->name2 != NULL) &&
- (strcmp(subcs->name2, name2) == 0))
- break;
+ if (strcmp(subcs->name1, name1) != 0) continue;
+ if (!subcs->name2 && name2) continue;
+ if (subcs->name2 && !name2) continue;
+
+ if (!subcs->name2 && !name2) break;
+
+ if (strcmp(subcs->name2, name2) == 0) break;
}
return cf_itemtosection(ci);
{
CONF_ITEM *ci;
+ if (!section) return NULL;
+
/*
* If subsection is NULL this must be a first time run
* Find the subsection with correct name
CONF_SECTION *subsection,
const char *name1)
{
+ if (!section) return NULL;
+
if (!section->item.parent) return NULL;
return cf_subsection_find_next(section->item.parent, subsection, name1);
CONF_ITEM *cf_item_find_next(CONF_SECTION *section, CONF_ITEM *item)
{
+ if (!section) return NULL;
+
/*
* If item is NULL this must be a first time run
* Return the first item
}
}
+CONF_SECTION *cf_item_parent(CONF_ITEM *ci)
+{
+ if (!ci) return NULL;
+
+ return ci->parent;
+}
+
int cf_section_lineno(CONF_SECTION *section)
{
return cf_sectiontoitem(section)->lineno;
return cf_data_add_internal(cs, name, data, data_free, 0);
}
-
+#if 0
/*
* Copy CONF_DATA from src to dst
*/
return 1; /* rcode means anything? */
}
+#endif
int cf_section_template(CONF_SECTION *cs, CONF_SECTION *template)
{
}
-void cf_log_info(UNUSED CONF_SECTION *cs, const char *fmt, ...)
+void cf_log_info(CONF_SECTION *cs, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
- if (debug_flag > 1 && cf_log_config) vradlog(L_DBG, fmt, ap);
+ if (debug_flag > 1 && cf_log_config && cs) vradlog(L_DBG, fmt, ap);
va_end(ap);
}
/*
* Wrapper to simplify the code.
*/
-void cf_log_module(UNUSED CONF_SECTION *cs, const char *fmt, ...)
+void cf_log_module(CONF_SECTION *cs, const char *fmt, ...)
{
va_list ap;
char buffer[256];
va_start(ap, fmt);
- if (debug_flag > 1 && cf_log_modules) {
+ if (debug_flag > 1 && cf_log_modules && cs) {
vsnprintf(buffer, sizeof(buffer), fmt, ap);
radlog(L_DBG, " Module: %s", buffer);
va_end(ap);
}
+const CONF_PARSER *cf_section_parse_table(CONF_SECTION *cs)
+{
+ if (!cs) return NULL;
+
+ return cs->variables;
+}
#if 0
/*
return dump_config_section(cs, 0);
}
#endif
+
+static const char *cf_pair_print_value(const CONF_PAIR *cp,
+ char *buffer, size_t buflen)
+{
+ char *p;
+
+ if (!cp->value) return "";
+
+ switch (cp->value_type) {
+ default:
+ case T_BARE_WORD:
+ snprintf(buffer, buflen, "%s", cp->value);
+ break;
+
+ case T_SINGLE_QUOTED_STRING:
+ snprintf(buffer, buflen, "'%s'", cp->value);
+ break;
+
+ case T_DOUBLE_QUOTED_STRING:
+ buffer[0] = '"';
+ fr_print_string(cp->value, strlen(cp->value),
+ buffer + 1, buflen - 3);
+ p = buffer + strlen(buffer); /* yuck... */
+ p[0] = '"';
+ p[1] = '\0';
+ break;
+ }
+
+ return buffer;
+}
+
+
+int cf_pair2xml(FILE *fp, const CONF_PAIR *cp)
+{
+ fprintf(fp, "<%s>", cp->attr);
+ if (cp->value) {
+ char buffer[2048];
+
+ char *p = buffer;
+ const char *q = cp->value;
+
+ while (*q && (p < (buffer + sizeof(buffer) - 1))) {
+ if (q[0] == '&') {
+ memcpy(p, "&", 4);
+ p += 5;
+
+ } else if (q[0] == '<') {
+ memcpy(p, "<", 4);
+ p += 4;
+
+ } else if (q[0] == '>') {
+ memcpy(p, ">", 4);
+ p += 4;
+
+ } else {
+ *(p++) = *q;
+ }
+ q++;
+ }
+
+ *p = '\0';
+ fprintf(fp, "%s", buffer);
+ }
+
+ fprintf(fp, "</%s>\n", cp->attr);
+
+ return 1;
+}
+
+int cf_section2xml(FILE *fp, const CONF_SECTION *cs)
+{
+ CONF_ITEM *ci, *next;
+
+ /*
+ * Section header
+ */
+ fprintf(fp, "<%s>\n", cs->name1);
+ if (cs->name2) {
+ fprintf(fp, "<_name2>%s</_name2>\n", cs->name2);
+ }
+
+ /*
+ * Loop over contents.
+ */
+ for (ci = cs->children; ci; ci = next) {
+ next = ci->next;
+
+ switch (ci->type) {
+ case CONF_ITEM_PAIR:
+ if (!cf_pair2xml(fp, (CONF_PAIR *) ci)) return 0;
+ break;
+
+ case CONF_ITEM_SECTION:
+ if (!cf_section2xml(fp, (CONF_SECTION *) ci)) return 0;
+ break;
+
+ default: /* should really be an error. */
+ break;
+
+ }
+ }
+
+ fprintf(fp, "</%s>\n", cs->name1);
+
+ return 1; /* success */
+}
+
+int cf_pair2file(FILE *fp, const CONF_PAIR *cp)
+{
+ char buffer[2048];
+
+ fprintf(fp, "\t%s = %s\n", cp->attr,
+ cf_pair_print_value(cp, buffer, sizeof(buffer)));
+
+ return 1;
+}
+
+int cf_section2file(FILE *fp, const CONF_SECTION *cs)
+{
+ const CONF_ITEM *ci, *next;
+
+ /*
+ * Section header
+ */
+ if (!cs->name2) {
+ fprintf(fp, "%s {\n", cs->name1);
+ } else {
+ fprintf(fp, "%s %s {\n",
+ cs->name1, cs->name2);
+ }
+
+ /*
+ * Loop over contents.
+ */
+ for (ci = cs->children; ci; ci = next) {
+ next = ci->next;
+
+ switch (ci->type) {
+ case CONF_ITEM_PAIR:
+ if (!cf_pair2file(fp, (const CONF_PAIR *) ci)) return 0;
+ break;
+
+ case CONF_ITEM_SECTION:
+ if (!cf_section2file(fp, (const CONF_SECTION *) ci)) return 0;
+ break;
+
+ default: /* should really be an error. */
+ break;
+
+ }
+ }
+
+ fprintf(fp, "}\n");
+
+ return 1; /* success */
+}