📄 conffile.c
字号:
*q = '\0'; next = cf_section_sub_find(cs, p); *q = '.'; } if (!next) break; /* it MAY be a pair in this section! */ cs = next; p = q + 1; } if (!*p) goto no_such_item; retry: /* * Find it in the current referenced * section. */ cp = cf_pair_find(cs, p); if (cp) return cf_pairtoitem(cp); next = cf_section_sub_find(cs, p); if (next) return cf_sectiontoitem(next); /* * "foo" is "in the current section, OR in main". */ if ((p == name) && (cs != parentcs)) { cs = parentcs; goto retry; }no_such_item: DEBUG2("WARNING: No such configuration item %s", ptr); return NULL;}CONF_SECTION *cf_top_section(CONF_SECTION *cs){ while (cs->item.parent != NULL) { cs = cs->item.parent; } return cs;}/* * Expand the variables in an input string. */static const char *cf_expand_variables(const char *cf, int *lineno, CONF_SECTION *outercs, char *output, const char *input){ char *p; const char *end, *ptr; const CONF_SECTION *parentcs; char name[8192]; /* * Find the master parent conf section. * We can't use mainconfig.config, because we're in the * process of re-building it, and it isn't set up yet... */ parentcs = cf_top_section(outercs); p = output; ptr = input; while (*ptr) { /* * Ignore anything other than "${" */ if ((*ptr == '$') && (ptr[1] == '{')) { CONF_ITEM *ci; CONF_PAIR *cp; /* * FIXME: Add support for ${foo:-bar}, * like in xlat.c */ /* * Look for trailing '}', and log a * warning for anything that doesn't match, * and exit with a fatal error. */ end = strchr(ptr, '}'); if (end == NULL) { *p = '\0'; radlog(L_INFO, "%s[%d]: Variable expansion missing }", cf, *lineno); return NULL; } ptr += 2; /* * Can't really happen because input lines are * capped at 8k, which is sizeof(name) */ if ((size_t) (end - ptr) >= sizeof(name)) { radlog(L_ERR, "%s[%d]: Reference string is too large", cf, *lineno); return NULL; } memcpy(name, ptr, end - ptr); name[end - ptr] = '\0'; ci = cf_reference_item(parentcs, outercs, name); if (!ci || (ci->type != CONF_ITEM_PAIR)) { radlog(L_ERR, "%s[%d]: Reference \"%s\" not found", cf, *lineno, input); return NULL; } /* * Substitute the value of the variable. */ cp = cf_itemtopair(ci); strcpy(p, cp->value); p += strlen(p); ptr = end + 1; } else if (memcmp(ptr, "$ENV{", 5) == 0) { char *env; ptr += 5; /* * Look for trailing '}', and log a * warning for anything that doesn't match, * and exit with a fatal error. */ end = strchr(ptr, '}'); if (end == NULL) { *p = '\0'; radlog(L_INFO, "%s[%d]: Environment variable expansion missing }", cf, *lineno); return NULL; } /* * Can't really happen because input lines are * capped at 8k, which is sizeof(name) */ if ((size_t) (end - ptr) >= sizeof(name)) { radlog(L_ERR, "%s[%d]: Environment variable name is too large", cf, *lineno); return NULL; } memcpy(name, ptr, end - ptr); name[end - ptr] = '\0'; /* * Get the environment variable. * If none exists, then make it an empty string. */ env = getenv(name); if (env == NULL) { *name = '\0'; env = name; } strcpy(p, env); p += strlen(p); ptr = end + 1; } else { /* * Copy it over verbatim. */ *(p++) = *(ptr++); } } /* loop over all of the input string. */ *p = '\0'; return output;}/* * Parses an item (not a CONF_ITEM) into the specified format, * with a default value. * * Returns -1 on error, 0 for correctly parsed, and 1 if the * default value was used. Note that the default value will be * used ONLY if the CONF_PAIR is NULL. */int cf_item_parse(CONF_SECTION *cs, const char *name, int type, void *data, const char *dflt){ int rcode = 0; char **q; const char *value; fr_ipaddr_t ipaddr; const CONF_PAIR *cp; char ipbuf[128]; cp = cf_pair_find(cs, name); if (cp) { value = cp->value; } else if (!dflt) { return 1; /* nothing to parse, return default value */ } else { rcode = 1; value = dflt; } if (!value) { return 0; } switch (type) { case PW_TYPE_BOOLEAN: /* * Allow yes/no and on/off */ if ((strcasecmp(value, "yes") == 0) || (strcasecmp(value, "on") == 0)) { *(int *)data = 1; } else if ((strcasecmp(value, "no") == 0) || (strcasecmp(value, "off") == 0)) { *(int *)data = 0; } else { *(int *)data = 0; radlog(L_ERR, "Bad value \"%s\" for boolean variable %s", value, name); return -1; } cf_log_info(cs, "\t%s = %s", name, value); break; case PW_TYPE_INTEGER: *(int *)data = strtol(value, 0, 0); cf_log_info(cs, "\t%s = %d", name, *(int *)data); break; case PW_TYPE_STRING_PTR: q = (char **) data; if (*q != NULL) { free(*q); } /* * Expand variables which haven't already been * expanded automagically when the configuration * file was read. */ if (value == dflt) { char buffer[8192]; int lineno = cs->item.lineno; /* * FIXME: sizeof(buffer)? */ value = cf_expand_variables("?", &lineno, cs, buffer, value); if (!value) return -1; } cf_log_info(cs, "\t%s = \"%s\"", name, value ? value : "(null)"); *q = value ? strdup(value) : NULL; break; /* * This is the same as PW_TYPE_STRING_PTR, * except that we also "stat" the file, and * cache the result. */ case PW_TYPE_FILENAME: q = (char **) data; if (*q != NULL) { free(*q); } /* * Expand variables which haven't already been * expanded automagically when the configuration * file was read. */ if (value == dflt) { char buffer[8192]; int lineno = cs->item.lineno; /* * FIXME: sizeof(buffer)? */ value = cf_expand_variables("?", &lineno, cs, buffer, value); if (!value) return -1; } cf_log_info(cs, "\t%s = \"%s\"", 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 (0 && *q) { struct stat buf; if (stat(*q, &buf) == 0) { time_t *mtime; mtime = rad_malloc(sizeof(*mtime)); *mtime = buf.st_mtime; /* FIXME: error? */ cf_data_add_internal(cs, *q, mtime, free, PW_TYPE_FILENAME); } } break; case PW_TYPE_IPADDR: /* * Allow '*' as any address */ if (strcmp(value, "*") == 0) { *(uint32_t *) data = htonl(INADDR_ANY); cf_log_info(cs, "\t%s = *", name); break; } if (ip_hton(value, AF_INET, &ipaddr) < 0) { radlog(L_ERR, "Can't find IP address for host %s", value); return -1; } if (strspn(value, "0123456789.") == strlen(value)) { cf_log_info(cs, "\t%s = %s", name, value); } else { cf_log_info(cs, "\t%s = %s IP address [%s]", name, value, ip_ntoh(&ipaddr, ipbuf, sizeof(ipbuf))); } *(uint32_t *) data = ipaddr.ipaddr.ip4addr.s_addr; break; case PW_TYPE_IPV6ADDR: if (ip_hton(value, AF_INET6, &ipaddr) < 0) { 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))); 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 */ return rcode;}static const char *parse_spaces = " ";/* * Parse a configuration section into user-supplied variables. */int cf_section_parse(CONF_SECTION *cs, void *base, const CONF_PARSER *variables){ int i; void *data; cs->variables = variables; /* this doesn't hurt anything */ if (!cs->name2) { cf_log_info(cs, "%.*s%s {", cs->depth, parse_spaces, cs->name1); } else { cf_log_info(cs, "%.*s%s %s {", cs->depth, parse_spaces, cs->name1, cs->name2); } /* * Handle the known configuration parameters. */ for (i = 0; variables[i].name != NULL; i++) { /* * Handle subsections specially */ if (variables[i].type == PW_TYPE_SUBSECTION) { CONF_SECTION *subcs; subcs = cf_section_sub_find(cs, variables[i].name); /* * If the configuration section is NOT there, * then ignore it. * * FIXME! This is probably wrong... we should * probably set the items to their default values. */ if (!subcs) continue; if (!variables[i].dflt) { DEBUG2("Internal sanity check 1 failed in cf_section_parse"); goto error; } if (cf_section_parse(subcs, base, (const CONF_PARSER *) variables[i].dflt) < 0) { goto error; } continue; } /* else it's a CONF_PAIR */ if (variables[i].data) { data = variables[i].data; /* prefer this. */ } else if (base) { data = ((char *)base) + variables[i].offset; } else { DEBUG2("Internal sanity check 2 failed in cf_section_parse"); goto error; } /* * Parse the pair we found, or a default value. */ if (cf_item_parse(cs, variables[i].name, variables[i].type, data, variables[i].dflt) < 0) { goto error; } } /* for all variables in the configuration section */ cf_log_info(cs, "%.*s}", cs->depth, parse_spaces); cs->base = base; return 0; error: cf_log_info(cs, "%.*s}", cs->depth, parse_spaces); cf_section_parse_free(cs, base); return -1;}/* * Sanity check the "if" or "elsif", presuming that the first '(' * has already been eaten. * * We're not really parsing it here, just checking if it's mostly * well-formed. */static int condition_looks_ok(const char **ptr){ int num_braces = 1; int quote = 0; const char *p = *ptr; while (*p) { if (quote) { if (*p == quote) { p++; quote = 0; continue; } if (*p == '\\') { if (!p[1]) { return 0; /* no trailing slash */ } p += 2; continue; } p++; continue; } switch (*p) { case '\\': if (!p[1]) { return 0; /* no trailing slash */ } p += 2; continue; case '(': num_braces++; p++; continue; case ')': if (num_braces == 1) { const char *q = p + 1; /* * Validate that there isn't much * else after the closing brace. */ while ((*q == ' ') || (*q == '\t')) q++; /* * Parse error. */ if (*q != '{') { return 0; } *ptr = p + 1; /* include the trailing ')' */ return 1; } num_braces--; p++; continue; case '"': case '\'': case '/': case '`': quote = *p; /* FALL-THROUGH */ default: p++; break; } } return 0;}static const char *cf_local_file(CONF_SECTION *cs, const char *local, char *buffer, size_t bufsize){ size_t dirsize; const char *p; CONF_SECTION *parentcs = cf_top_section(cs); p = strrchr(parentcs->item.filename, FR_DIR_SEP); if (!p) return local; dirsize = (p - parentcs->item.filename) + 1; if ((dirsize + strlen(local)) >= bufsize) { return NULL; } memcpy(buffer, parentcs->item.filename, dirsize); strlcpy(buffer + dirsize, local, bufsize - dirsize); return buffer;}/* * Read a part of the config file. */static int cf_section_read(const char *filename, int *lineno, FILE *fp, CONF_SECTION *current){ CONF_SECTION *this, *css; CONF_PAIR *cpn; const char *ptr; const char *value; char buf[8192]; char buf1[8192]; char buf2[8192]; char buf3[8192]; int t1, t2, t3; char *cbuf = buf; size_t len; this = current; /* add items here */ /* * Read, checking for line continuations ('\\' at EOL) */ for (;;) { int eof; /* * Get data, and remember if we are at EOF. */ eof = (fgets(cbuf, sizeof(buf) - (cbuf - buf), fp) == NULL); (*lineno)++; /* * We read the entire 8k worth of data: complain. * Note that we don't care if the last character * is \n: it's still forbidden. This means that * the maximum allowed length of text is 8k-1, which * should be plenty. */ len = strlen(cbuf); if ((cbuf + len + 1) >= (buf + sizeof(buf))) { radlog(L_ERR, "%s[%d]: Line too long", filename, *lineno); return -1; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -