/** * @brief Print data as hex, not as VALUE. */ static size_t xlat_hex(UNUSED void *instance, REQUEST *request, const char *fmt, char *out, size_t outlen) { size_t i; VALUE_PAIR *vp; uint8_t buffer[MAX_STRING_LEN]; ssize_t ret; size_t len; while (isspace((int) *fmt)) fmt++; if (!radius_get_vp(request, fmt, &vp) || !vp) { *out = '\0'; return 0; } ret = rad_vp2data(vp, buffer, sizeof(buffer)); len = (size_t) ret; /* * Don't truncate the data. */ if ((ret < 0 ) || (outlen < (len * 2))) { *out = 0; return 0; } for (i = 0; i < len; i++) { snprintf(out + 2*i, 3, "%02x", buffer[i]); } return len * 2; }
/** Crappy temporary function to add attribute ref support to xlats * * This needs to die, and hopefully will die, when xlat functions accept * xlat node structures. * * Provides either a pointer to a buffer which contains the value of the reference VALUE_PAIR * in an architecture independent format. Or a pointer to the start of the fmt string. * * The pointer is only guaranteed to be valid between calls to xlat_fmt_to_ref, * and so long as the source VALUE_PAIR is not freed. * * @param out where to write a pointer to the buffer to the data the xlat function needs to work on. * @param request current request. * @param fmt string. * @returns the length of the data or -1 on error. */ ssize_t xlat_fmt_to_ref(uint8_t const **out, REQUEST *request, char const *fmt) { VALUE_PAIR *vp; while (isspace((int) *fmt)) fmt++; if (fmt[0] == '&') { if ((radius_get_vp(&vp, request, fmt + 1) < 0) || !vp) { *out = NULL; return -1; } return rad_vp2data(out, vp); } *out = (uint8_t const *)fmt; return strlen(fmt); }
/** Print data as string, if possible. * * If attribute "Foo" is defined as "octets" it will normally * be printed as 0x0a0a0a. The xlat "%{string:Foo}" will instead * expand to "\n\n\n" */ static ssize_t xlat_string(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen) { size_t len; ssize_t ret; VALUE_PAIR *vp; uint8_t const *p; while (isspace((int) *fmt)) fmt++; if (*fmt == '&') fmt++; if (outlen < 3) { nothing: *out = '\0'; return 0; } if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) goto nothing; ret = rad_vp2data(&p, vp); if (ret < 0) { return ret; } switch (vp->da->type) { case PW_TYPE_OCTETS: len = fr_print_string((char const *) p, vp->length, out, outlen); break; case PW_TYPE_STRING: len = strlcpy(out, vp->vp_strvalue, outlen); break; default: len = fr_print_string((char const *) p, ret, out, outlen); break; } return len; }
/** * @brief Print data as base64, not as VALUE */ static size_t xlat_base64(UNUSED void *instance, REQUEST *request, char *fmt, char *out, size_t outlen, UNUSED RADIUS_ESCAPE_STRING func) { VALUE_PAIR *vp; uint8_t buffer[MAX_STRING_LEN]; ssize_t ret; size_t len; size_t enc; while (isspace((int) *fmt)) fmt++; if (!radius_get_vp(request, fmt, &vp) || !vp) { *out = '\0'; return 0; } ret = rad_vp2data(vp, buffer, sizeof(buffer)); if (ret < 0) { *out = 0; return 0; } len = (size_t) ret; enc = FR_BASE64_ENC_LENGTH(len); /* * Don't truncate the data. */ if (outlen < (enc + 1)) { *out = 0; return 0; } fr_base64_encode(buffer, len, out, outlen); return enc; }
/** Print data as base64, not as VALUE * */ static size_t xlat_base64(UNUSED void *instance, REQUEST *request, const char *fmt, char *out, size_t outlen) { VALUE_PAIR *vp; uint8_t buffer[MAX_STRING_LEN]; ssize_t ret; while (isspace((int) *fmt)) fmt++; if ((radius_get_vp(request, fmt, &vp) < 0) || !vp) { *out = '\0'; return 0; } ret = rad_vp2data(vp, buffer, sizeof(buffer)); if (ret < 0) { *out = 0; return 0; } return fr_base64_encode(buffer, (size_t) ret, out, outlen); }
/** Print data as hex, not as VALUE. * */ static ssize_t xlat_hex(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen) { size_t i; VALUE_PAIR *vp; uint8_t const *p; ssize_t ret; size_t len; while (isspace((int) *fmt)) fmt++; if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) { *out = '\0'; return -1; } ret = rad_vp2data(&p, vp); if (ret < 0) { return ret; } len = (size_t) ret; /* * Don't truncate the data. */ if ((ret < 0 ) || (outlen < (len * 2))) { *out = 0; return 0; } for (i = 0; i < len; i++) { snprintf(out + 2*i, 3, "%02x", p[i]); } return len * 2; }
/** Print out attribute info * * Prints out all instances of a current attribute, or all attributes in a list. * * At higher debugging levels, also prints out alternative decodings of the same * value. This is helpful to determine types for unknown attributes of long * passed vendors, or just crazy/broken NAS. * * It's also useful for exposing issues in the packet decoding functions, as in * some cases they get fed random garbage data. * * This expands to a zero length string. */ static ssize_t xlat_debug_attr(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, UNUSED size_t outlen) { VALUE_PAIR *vp, **vps; REQUEST *current; value_pair_tmpl_t vpt; vp_cursor_t cursor; char buffer[1024]; if (!RDEBUG_ENABLED2) { *out = '\0'; return -1; } while (isspace((int) *fmt)) fmt++; if (*fmt == '&') fmt++; if (radius_parse_attr(fmt, &vpt, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) { return -1; } current = request; if (radius_request(¤t, vpt.request) < 0) return -2; vps = radius_list(current, vpt.list); if (!vps) { return -2; } RIDEBUG("Attributes matching \"%s\"", fmt); vp = fr_cursor_init(&cursor, vps); if (vpt.da) { vp = fr_cursor_next_by_num(&cursor, vpt.da->attr, vpt.da->vendor, TAG_ANY); } while (vp) { DICT_ATTR *dac = NULL; DICT_VENDOR *dv; VALUE_PAIR *vpc = NULL; FR_NAME_NUMBER const *type; vp_prints_value(buffer, sizeof(buffer), vp, '\''); if (vp->da->flags.has_tag) { RIDEBUG2("\t%s:%s:%i %s %s", fr_int2str(pair_lists, vpt.list, "<INVALID>"), vp->da->name, vp->tag, fr_int2str(fr_tokens, vp->op, "<INVALID>"), buffer); } else { RIDEBUG2("\t%s:%s %s %s", fr_int2str(pair_lists, vpt.list, "<INVALID>"), vp->da->name, fr_int2str(fr_tokens, vp->op, "<INVALID>"), buffer); } if (!RDEBUG_ENABLED3) { goto next_vp; } if (vp->da->vendor) { dv = dict_vendorbyvalue(vp->da->vendor); RDEBUG3("\t\tvendor : %i (%s)", vp->da->vendor, dv ? dv->name : "unknown"); } RDEBUG3("\t\ttype : %s", fr_int2str(dict_attr_types, vp->da->type, "<INVALID>")); RDEBUG3("\t\tlength : %zu", vp->length); dac = talloc_memdup(request, vp->da, sizeof(DICT_ATTR)); if (!dac) { return -1; } dac->flags.vp_free = 0; if (!RDEBUG_ENABLED4) { goto next_vp; } type = dict_attr_types; while (type->name) { int pad; ssize_t len; uint8_t const *data = NULL; vpc = NULL; if ((PW_TYPE) type->number == vp->da->type) { goto next_type; } switch (type->number) { case PW_TYPE_INVALID: /* Not real type */ case PW_TYPE_MAX: /* Not real type */ case PW_TYPE_EXTENDED: /* Not safe/appropriate */ case PW_TYPE_LONG_EXTENDED: /* Not safe/appropriate */ case PW_TYPE_TLV: /* Not safe/appropriate */ case PW_TYPE_VSA: /* @fixme We need special behaviour for these */ goto next_type; default: break; } dac->type = type->number; len = rad_vp2data(&data, vp); if (len < 0) { goto next_type; } if (data2vp(NULL, NULL, NULL, dac, data, len, len, &vpc) < 0) { goto next_type; } /* * data2vp has knowledge of expected format lengths, if the length * from rad_vp2data doesn't match, it encodes the attribute * as raw octets. This results in many useless debug lines with * the same hex string. */ if ((type->number != PW_TYPE_OCTETS) && (vpc->da->type == PW_TYPE_OCTETS)) { goto next_type; } if (!vp_prints_value(buffer, sizeof(buffer), vpc, '\'')) { goto next_type; } if ((pad = (11 - strlen(type->name))) < 0) { pad = 0; } /* * @fixme: if the value happens to decode as a VSA * (someone put a VSA into a VSA?), we probably to print * extended info for that/reparse */ RDEBUG4("\t\tas %s%*s: %s", type->name, pad, " ", buffer); next_type: talloc_free(vpc); type++; } next_vp: talloc_free(dac); if (vpt.da) { vp = fr_cursor_next_by_num(&cursor, vpt.da->attr, vpt.da->vendor, TAG_ANY); } else { vp = fr_cursor_next(&cursor); } } *out = '\0'; return 0; }