From: Alan T. DeKok Date: Sun, 29 Apr 2012 08:16:44 +0000 (+0200) Subject: Allow referencing tags in xlat X-Git-Tag: release_2_2_0~131 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=33e9e1f7fbf5e63baa0fbd734e570474bee10878;p=thirdparty%2Ffreeradius-server.git Allow referencing tags in xlat %{Tunnel-Type:1} works, and is the same as %{Tunnel-Type:1[0]} All of the other expansions now work, too --- diff --git a/src/main/xlat.c b/src/main/xlat.c index ca5207d3250..64a1d8d338b 100644 --- a/src/main/xlat.c +++ b/src/main/xlat.c @@ -95,6 +95,23 @@ static int valuepair2str(char * out,int outlen,VALUE_PAIR * pair, return strlen(out); } +static VALUE_PAIR *pairfind_tag(VALUE_PAIR *vps, int attr, int tag) +{ + VALUE_PAIR *vp = vps; + +redo: + if (!vp) return NULL; + + vp = pairfind(vp, attr); + if (!tag) return vp; + + if (!vp->flags.has_tag) return NULL; + + if (vp->flags.tag == tag) return vp; + + vp = vp->next; + goto redo; +} /* * Dynamically translate for check:, request:, reply:, etc. @@ -167,56 +184,95 @@ static size_t xlat_packet(void *instance, REQUEST *request, da = dict_attrbyname(fmt); if (!da) { int do_number = FALSE; - size_t count; - const char *p; + int do_array = FALSE; + int do_count = FALSE; + int do_all = FALSE; + int tag = 0; + size_t count, total; + char *p; char buffer[256]; if (strlen(fmt) > sizeof(buffer)) return 0; - p = strchr(fmt, '['); - if (!p) { - p = strchr(fmt, '#'); - if (!p) return 0; + strlcpy(buffer, fmt, sizeof(buffer)); + + /* + * %{Attribute-name#} - print integer version of it. + */ + p = buffer + strlen(buffer) - 1; + if (*p == '#') { + *p = '\0'; do_number = TRUE; } - strlcpy(buffer, fmt, p - fmt + 1); + /* + * %{Attribute-Name:tag} - get the name with the specified + * value of the tag. + */ + p = strchr(buffer, ':'); + if (p) { + tag = atoi(p + 1); + *p = '\0'; + p++; - da = dict_attrbyname(buffer); - if (!da) return 0; + } else { + /* + * Allow %{Attribute-Name:tag[...]} + */ + p = buffer; + } - if (do_number) { - vp = pairfind(vps, da->attr); - if (!vp) return 0; + /* + * %{Attribute-Name[...] does more stuff + */ + p = strchr(p, '['); + if (p) { + do_array = TRUE; + if (p[1] == '#') { + do_count = TRUE; + } else if (p[1] == '*') { + do_all = TRUE; + } else { + count = atoi(p + 1); + p += 1 + strspn(p + 1, "0123456789"); + if (*p != ']') { + RDEBUG2("xlat: Invalid array reference in string at %s %s", + fmt, p); + return 0; + } + } + *p = '\0'; + } - switch (da->type) { - default: - break; + /* + * We COULD argue about %{Attribute-Name[#]#} etc. + * But that looks like more work than it's worth. + */ - case PW_TYPE_INTEGER: - case PW_TYPE_DATE: - case PW_TYPE_SHORT: - case PW_TYPE_BYTE: - snprintf(out, outlen, "%u", vp->lvalue); - return strlen(out); - } + da = dict_attrbyname(buffer); + if (!da) return 0; + /* + * No array, print the tagged attribute. + */ + if (!do_array) { + vp = pairfind_tag(vps, da->attr, tag); goto just_print; } + total = 0; + /* - * %{Attribute-Name[#]} returns the count of - * attributes of that name in the list. + * Array[#] - return the total */ - if ((p[1] == '#') && (p[2] == ']')) { - count = 0; - - for (vp = pairfind(vps, da->attr); + if (do_count) { + for (vp = pairfind_tag(vps, da->attr, tag); vp != NULL; - vp = pairfind(vp->next, da->attr)) { - count++; + vp = pairfind_tag(vp->next, da->attr, tag)) { + total++; } - snprintf(out, outlen, "%d", (int) count); + + snprintf(out, outlen, "%d", (int) total); return strlen(out); } @@ -224,12 +280,10 @@ static size_t xlat_packet(void *instance, REQUEST *request, * %{Attribute-Name[*]} returns ALL of the * the attributes, separated by a newline. */ - if ((p[1] == '*') && (p[2] == ']')) { - int total = 0; - - for (vp = pairfind(vps, da->attr); + if (do_all) { + for (vp = pairfind_tag(vps, da->attr, tag); vp != NULL; - vp = pairfind(vp->next, da->attr)) { + vp = pairfind_tag(vp->next, da->attr, tag)) { count = valuepair2str(out, outlen - 1, vp, da->type, func); rad_assert(count <= outlen); total += count + 1; @@ -245,33 +299,38 @@ static size_t xlat_packet(void *instance, REQUEST *request, return total; } - count = atoi(p + 1); - - /* - * Skip the numbers. - */ - p += 1 + strspn(p + 1, "0123456789"); - if (*p != ']') { - RDEBUG2("xlat: Invalid array reference in string at %s %s", - fmt, p); - return 0; - } - /* * Find the N'th value. */ - for (vp = pairfind(vps, da->attr); + for (vp = pairfind_tag(vps, da->attr, tag); vp != NULL; - vp = pairfind(vp->next, da->attr)) { - if (count == 0) break; - count--; + vp = pairfind_tag(vp->next, da->attr, tag)) { + if (total == count) break; + total++; + if (total > count) { + vp = NULL; + break; + } } /* * Non-existent array reference. */ - if (!vp) return 0; just_print: + if (do_number) { + if ((vp->type != PW_TYPE_IPADDR) && + (vp->type != PW_TYPE_INTEGER) && + (vp->type != PW_TYPE_SHORT) && + (vp->type != PW_TYPE_BYTE) && + (vp->type != PW_TYPE_DATE)) { + *out = '\0'; + return 0; + } + + return snprintf(out, outlen, "%u", vp->vp_integer); + } + + if (!vp) return 0; return valuepair2str(out, outlen, vp, da->type, func); } @@ -638,6 +697,7 @@ static int xlat_cmp(const void *a, const void *b) ((const xlat_t *)a)->length); } + /* * find the appropriate registered xlat function. */ @@ -645,15 +705,6 @@ static xlat_t *xlat_find(const char *module) { xlat_t my_xlat; - /* - * Look for dictionary attributes first. - */ - if ((dict_attrbyname(module) != NULL) || - (strchr(module, '[') != NULL) || - (strchr(module, '#') != NULL)) { - module = "request"; - } - strlcpy(my_xlat.module, module, sizeof(my_xlat.module)); my_xlat.length = strlen(my_xlat.module); @@ -833,7 +884,7 @@ static int decode_attribute(const char **from, char **to, int freespace, RADIUS_ESCAPE_STRING func) { int do_length = 0; - char *xlat_name, *xlat_str; + char *module_name, *xlat_str; char *p, *q, *l, *next = NULL; int retlen=0; const xlat_t *c; @@ -961,7 +1012,7 @@ static int decode_attribute(const char **from, char **to, int freespace, /* * See if we're supposed to expand a module name. */ - xlat_name = NULL; + module_name = NULL; for (l = p; *l != '\0'; l++) { if (*l == '\\') { l++; @@ -969,7 +1020,9 @@ static int decode_attribute(const char **from, char **to, int freespace, } if (*l == ':') { - xlat_name = p; /* start of name */ + if (isdigit(l[1])) break; + + module_name = p; /* start of name */ *l = '\0'; p = l + 1; break; @@ -985,8 +1038,13 @@ static int decode_attribute(const char **from, char **to, int freespace, * %{name} is a simple attribute reference, * or regex reference. */ - if (!xlat_name) { - xlat_name = xlat_str = p; + if (!module_name) { + if (isdigit(*p)) { + module_name = xlat_str = p; + } else { + module_name = internal_xlat[1]; + xlat_str = p; + } goto do_xlat; } @@ -997,7 +1055,7 @@ static int decode_attribute(const char **from, char **to, int freespace, RDEBUG2("WARNING: Deprecated conditional expansion \":-\". See \"man unlang\" for details"); p++; - xlat_str = xlat_name; + xlat_str = module_name; next = p; goto do_xlat; } @@ -1011,33 +1069,34 @@ static int decode_attribute(const char **from, char **to, int freespace, xlat_str = p; do_xlat: - if ((c = xlat_find(xlat_name)) != NULL) { - if (!c->internal) RDEBUG3("radius_xlat: Running registered xlat function of module %s for string \'%s\'", - c->module, xlat_str); - retlen = c->do_xlat(c->instance, request, xlat_str, - q, freespace, func); - if (retlen > 0) { - if (do_length) { - snprintf(q, freespace, "%d", retlen); - retlen = strlen(q); - } - - } else if (next) { - /* - * Expand the second bit. - */ - RDEBUG2("\t... expanding second conditional"); - retlen = radius_xlat(q, freespace, next, request, func); + c = xlat_find(module_name); + if (!c) { + if (module_name == internal_xlat[1]) { + RDEBUG2("WARNING: Unknown Attribute \"%s\" in string expansion \"%%%s\"", module_name, *from); + } else { + RDEBUG2("WARNING: Unknown module \"%s\" in string expansion \"%%%s\"", module_name, *from); } - q += retlen; + return -1; + } - } else { + if (!c->internal) RDEBUG3("radius_xlat: Running registered xlat function of module %s for string \'%s\'", + c->module, xlat_str); + retlen = c->do_xlat(c->instance, request, xlat_str, + q, freespace, func); + if (retlen > 0) { + if (do_length) { + snprintf(q, freespace, "%d", retlen); + retlen = strlen(q); + } + + } else if (next) { /* - * No attribute by that name, return an error. + * Expand the second bit. */ - RDEBUG2("WARNING: Unknown module \"%s\" in string expansion \"%%%s\"", xlat_name, *from); - return -1; + RDEBUG2("\t... expanding second conditional"); + retlen = radius_xlat(q, freespace, next, request, func); } + q += retlen; done: *to = q;