it's not yet complete, but even the addition of the function caused significant issues. It should now be safe at least...
* Xlat expansions are not supported. Convert xlat to value box (if possible).
*/
if (vp->type == VT_XLAT) {
- if (fr_value_box_from_str(vp, &vp->data, vp->da->type, NULL, vp->xlat, -1, '\0', false) < 0) {
+ if (fr_value_box_from_str(vp, &vp->data,
+ vp->da->type, NULL,
+ vp->xlat, strlen(vp->xlat),
+ NULL, false) < 0) {
fr_perror("dhcpclient");
fr_radius_packet_free(&packet);
if (fp && (fp != stdin)) fclose(fp);
* Xlat expansions are not supported. Convert xlat to value box (if possible).
*/
if (vp->type == VT_XLAT) {
- if (fr_value_box_from_str(vp, &vp->data, vp->da->type, NULL, vp->xlat, -1, '\0', false) < 0) {
+ if (fr_value_box_from_str(vp, &vp->data,
+ vp->da->type, NULL,
+ vp->xlat, strlen(vp->xlat),
+ NULL, false) < 0) {
fr_perror("radclient");
goto error;
}
* Xlat expansions are not supported. Convert xlat to value box (if possible).
*/
if (vp->type == VT_XLAT) {
- if (fr_value_box_from_str(vp, &vp->data, vp->da->type, NULL, vp->xlat, -1, '\0', false) < 0) {
+ if (fr_value_box_from_str(vp, &vp->data, vp->da->type, NULL,
+ vp->xlat, strlen(vp->xlat),
+ NULL, false) < 0) {
fr_perror("radclient");
goto error;
}
fr_value_box_t box;
struct ProfilerState state;
- if (fr_value_box_from_str(NULL, &box, FR_TYPE_BOOL, NULL, info->argv[0], strlen(info->argv[0]), '\0', false) < 0) {
+ if (fr_value_box_from_str(NULL, &box, FR_TYPE_BOOL, NULL,
+ info->argv[0], strlen(info->argv[0]),
+ NULL, false) <= 0) {
fprintf(fp_err, "Failed setting profile status '%s' - %s\n", info->argv[0], fr_strerror());
return -1;
}
* Xlat expansions are not supported. Convert xlat to value box (if possible).
*/
if (vp->type == VT_XLAT) {
- if (fr_value_box_from_str(vp, &vp->data, vp->da->type, NULL, vp->xlat, -1, '\0', false) < 0) {
+ if (fr_value_box_from_str(vp, &vp->data, vp->da->type, NULL,
+ vp->xlat, strlen(vp->xlat),
+ NULL, false) < 0) {
fr_perror("radsniff");
return -1;
}
return -(slen);
}
- ret = fr_pair_value_from_str(vp, value, -1, '\0', true);
+ ret = fr_pair_value_from_str(vp, value, strlen(value), NULL, true);
if (ret < 0) {
slen = -(slen);
goto error;
fr_skip_whitespace(p);
- if (fr_value_box_from_str(box, box, FR_TYPE_STRING, NULL, p, -1, '"', false) < 0) {
+ if (fr_value_box_from_str(box, box, FR_TYPE_STRING, NULL,
+ p, strlen(p),
+ &fr_value_unescape_double,
+ false) < 0) {
talloc_free(box);
RETURN_OK_WITH_ERROR();
}
p = in + match_len;
fr_skip_whitespace(p);
- if (fr_value_box_from_str(box, box, type, NULL, p, -1, '"', false) < 0) {
+ if (fr_value_box_from_str(box, box, type, NULL,
+ p, strlen(p),
+ &fr_value_unescape_double,
+ false) < 0) {
error:
talloc_free(box);
RETURN_OK_WITH_ERROR();
* box as last time.
*/
box2 = talloc_zero(NULL, fr_value_box_t);
- if (fr_value_box_from_str(box2, box2, type, NULL, data, slen, '"', false) < 0) {
+ if (fr_value_box_from_str(box2, box2, type, NULL,
+ data, slen,
+ &fr_value_unescape_double, false) < 0) {
talloc_free(box2);
talloc_free(box);
RETURN_OK_WITH_ERROR();
continue;
}
MEM(vp = fr_pair_afrom_da(container, da));
- fr_pair_value_from_str(vp, q + 1, -1, '\0', true);
+ fr_pair_value_from_str(vp, q + 1, strlen(q + 1), NULL, true);
fr_pair_append(&container->vp_group, vp);
}
/* Set-up the fake request */
fake = request_alloc_internal(request, &(request_init_args_t){ .parent = request });
MEM(fr_pair_prepend_by_da(fake->request_ctx, &vp, &fake->request_pairs, attr_freeradius_proxied_to) >= 0);
- fr_pair_value_from_str(vp, "127.0.0.1", sizeof("127.0.0.1"), '\0', false);
+ fr_pair_value_from_str(vp, "127.0.0.1", sizeof("127.0.0.1") - 1, NULL, false);
/* Add the username to the fake request */
if (chbind->username) {
if (eap_tls_session->tls_session->info.version == TLS1_3_VERSION) {
prf_label->keying_prf_label = "EXPORTER_EAP_TLS_Key_Material";
- prf_label->keying_prf_label_len = sizeof("EXPORTER_EAP_TLS_Key_Material") -1;
+ prf_label->keying_prf_label_len = sizeof("EXPORTER_EAP_TLS_Key_Material") - 1;
prf_label->sessid_prf_label = "EXPORTER_EAP_TLS_Method-Id";
prf_label->sessid_prf_label_len = sizeof("EXPORTER_EAP_TLS_Method-Id") - 1;
MEM(vp = fr_pair_afrom_da(ctx, tmpl_da(map->lhs)));
if (fr_pair_value_from_str(vp, self->values[i]->bv_val,
- self->values[i]->bv_len, '\0', true) < 0) {
+ self->values[i]->bv_len, NULL, true) < 0) {
RPWDEBUG("Failed parsing value \"%pV\" for attribute %s",
fr_box_strvalue_len(self->values[i]->bv_val, self->values[i]->bv_len),
tmpl_da(map->lhs)->name);
* Parse the data to be sure it's well formed.
*/
if (fr_value_box_from_str(ctx, box, type,
- NULL, name, -1, quote, true) < 0) {
+ NULL,
+ name, strlen(name),
+ fr_value_unescape_by_char[(uint8_t)quote], true) < 0) {
fr_strerror_printf_push("Failed parsing argument '%s'", name);
return -1;
}
ret = fr_value_box_from_str(info->box[argc], info->box[argc],
type, NULL,
- word + offset, len - (offset << 1), quote, false);
+ word + offset, len - (offset << 1),
+ fr_value_unescape_by_char[(uint8_t)quote], false);
if (ret < 0) return -1;
/*
ret = fr_value_box_from_str(info->box[info->argc], info->box[info->argc],
type, NULL,
- word + offset, len - (offset << 1), quote, false);
+ word + offset, len - (offset << 1),
+ fr_value_unescape_by_char[(uint8_t)quote], false);
if (ret < 0) return -1;
info->argc++;
*word_p = word = p;
}
-static int cond_cast_tmpl(tmpl_t *vpt, fr_type_t *p_type, tmpl_t *other)
+static int cond_cast_tmpl(tmpl_t *vpt, fr_type_t type, tmpl_t *other)
{
fr_dict_attr_t const *da;
- fr_type_t type = *p_type;
fr_assert(type != FR_TYPE_NULL);
fr_assert(type < FR_TYPE_TLV);
- if (tmpl_is_unresolved(vpt)) {
- switch (type) {
- case FR_TYPE_IPV4_ADDR:
- if (strchr(vpt->name, '/') != NULL) {
- *p_type = type = FR_TYPE_IPV4_PREFIX;
- (void) tmpl_cast_set(other, type);
- }
- break;
-
- case FR_TYPE_IPV6_ADDR:
- if (strchr(vpt->name, '/') != NULL) {
- *p_type = type = FR_TYPE_IPV6_PREFIX;
- (void) tmpl_cast_set(other, type);
- }
- break;
-
- default:
- break;
- }
-
- } else if (tmpl_is_attr(vpt)) {
+ if (tmpl_is_attr(vpt)) {
(void) tmpl_cast_set(vpt, type);
return 0;
- } else if (!tmpl_is_data(vpt)) {
+ } else if (!tmpl_is_data(vpt) && !tmpl_is_unresolved(vpt)) {
/*
* Nothing to do.
*/
if (tmpl_cast_in_place(vpt, type, da) < 0) {
fr_strerror_printf("Failed parsing value as type '%s'",
- fr_table_str_by_value(fr_value_box_type_table,
- type, "??"));
+ fr_table_str_by_value(fr_value_box_type_table, type, "??"));
return -1;
}
/*
* Cast both sides to the promoted type.
*/
- if (cond_cast_tmpl(c->data.map->lhs, &cast_type, c->data.map->rhs) < 0) {
+ if (cond_cast_tmpl(c->data.map->lhs, cast_type, c->data.map->rhs) < 0) {
if (in) fr_sbuff_set(in, m_lhs);
return -1;
}
- if (cond_cast_tmpl(c->data.map->rhs, &cast_type, c->data.map->lhs) < 0) {
+ if (cond_cast_tmpl(c->data.map->rhs, cast_type, c->data.map->lhs) < 0) {
if (in) fr_sbuff_set(in, m_rhs);
return -1;
}
MEM(vp = fr_pair_afrom_da(ctx, tmpl_da(map->lhs)));
vp->op = map->op;
- if (fr_pair_value_from_str(vp, answer, -1, '"', false) < 0) {
+ if (fr_pair_value_from_str(vp, answer, strlen(answer), &fr_value_unescape_single, false) < 0) {
RPEDEBUG("Failed parsing exec output");
talloc_free(&vp);
return -2;
RDEBUG2("--> %s", str);
- rcode = fr_pair_value_from_str(n, str, -1, '\0', false);
+ rcode = fr_pair_value_from_str(n, str, strlen(str), NULL, false);
talloc_free(str);
if (rcode < 0) {
talloc_free(&n);
MEM(n = fr_pair_afrom_da(ctx, tmpl_da(map->lhs)));
- if (fr_pair_value_from_str(n, map->rhs->name, -1, '\0', false) < 0) {
+ if (fr_pair_value_from_str(n, map->rhs->name, strlen(map->rhs->name), NULL, false) < 0) {
rcode = 0;
talloc_free(n);
goto error;
if (fr_value_box_from_str(fr_dlist_head(&n->mod),
tmpl_value(fr_map_list_head(&n->mod)->rhs), type,
tmpl_da(mutated->lhs),
- mutated->rhs->name, mutated->rhs->len, mutated->rhs->quote, false)) {
+ mutated->rhs->name, mutated->rhs->len,
+ fr_value_unescape_by_quote[(uint8_t)mutated->rhs->quote], false)) {
RPEDEBUG("Assigning value to \"%s\" failed", tmpl_da(mutated->lhs)->name);
goto error;
}
MEM(vp = fr_pair_afrom_da(request->request_ctx, check_item->da));
vp->op = check_item->op;
- fr_pair_value_from_str(vp, value, -1, '"', false);
+ fr_pair_value_from_str(vp, value, strlen(value), &fr_value_unescape_single, false);
/*
* Paircmp returns 0 for failed comparison, 1 for succeeded -1 for error.
#endif
} data;
- fr_type_t _CONST cast;
- tmpl_rules_t _CONST rules;
+ fr_type_t _CONST cast; //!< Type we should interpret unresolved data with.
+ fr_dict_attr_t const * _CONST enumv; //!< Enumeration we should use when parsing unescaped
+ ///< i.e. barewords with an unknown format.
+
+ tmpl_rules_t _CONST rules; //!< The rules that were used when creating the tmpl.
+ ///< These are useful for multiple resolution passes as
+ ///< they ensure the correct parsing rules are applied.
};
typedef struct tmpl_cursor_ctx_s tmpl_pair_cursor_ctx_t;
#define tmpl_init_initialiser_list(_request, _list)\
{ \
.name = "static", \
- .len = sizeof("static"), \
+ .len = sizeof("static") - 1, \
.type = TMPL_TYPE_LIST, \
.quote = T_SINGLE_QUOTED_STRING, \
.data = { \
fr_sbuff_parse_rules_t const *p_rules,
tmpl_rules_t const *t_rules);
-ssize_t tmpl_cast_from_substr(fr_type_t *out, fr_sbuff_t *in); /* Parses cast string */
+ssize_t tmpl_cast_from_substr(fr_type_t *out, fr_sbuff_t *in); /* Parses cast string */
+
+int tmpl_cast_set(tmpl_t *vpt, fr_type_t type) CC_HINT(nonnull); /* Sets cast type */
-int tmpl_cast_set(tmpl_t *vpt, fr_type_t type); /* Sets cast type */
#ifdef HAVE_REGEX
ssize_t tmpl_regex_flags_substr(tmpl_t *vpt, fr_sbuff_t *in,
#define _TMPL_PRIVATE 1
-#include <freeradius-devel/server/tmpl.h>
#include <freeradius-devel/server/exec.h>
#include <freeradius-devel/server/exec_legacy.h>
-#include <freeradius-devel/util/proto.h>
+#include <freeradius-devel/server/tmpl.h>
#include <freeradius-devel/util/dlist.h>
+#include <freeradius-devel/util/proto.h>
+#include <freeradius-devel/util/value.h>
/** Resolve attribute #pair_list_t value to an attribute list.
*
* @fixme We need a way of signalling xlat not to escape things.
*/
ret = fr_value_box_from_str(tmp_ctx, &tmp, src_type, NULL,
- result, (size_t)slen, '"', false);
+ result, (size_t)slen,
+ &fr_value_unescape_double, false);
if (ret < 0) goto error;
fr_value_box_bstrndup_shallow(&value, NULL, tmp.vb_strvalue, tmp.vb_length, tmp.tainted);
* @fixme We need a way of signalling xlat not to escape things.
*/
ret = fr_value_box_from_str(tmp_ctx, &tmp, src_type, NULL,
- result, (size_t)slen, '"', false);
+ result, (size_t)slen,
+ &fr_value_unescape_double, false);
if (ret < 0) goto error;
fr_value_box_bstrndup_shallow(&value, NULL, tmp.vb_strvalue, tmp.vb_length, tmp.tainted);
}
MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA, T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
- if (fr_value_box_from_str(vpt, &vpt->data.literal, type, NULL,
- fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), '\0', false) < 0) {
+ if (fr_value_box_from_substr(vpt, &vpt->data.literal, type, NULL,
+ &FR_SBUFF_REPARSE(&our_in),
+ NULL, false) < 0) {
talloc_free(vpt);
goto error;
}
}
MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA, T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
- if (fr_value_box_from_str(vpt, &vpt->data.literal, type, NULL,
- fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), '\0', false) < 0) {
+ if (fr_value_box_from_substr(vpt, &vpt->data.literal, type, NULL,
+ &FR_SBUFF_REPARSE(&our_in),
+ NULL, false) < 0) {
talloc_free(vpt);
goto error;
}
* @{
*/
+/** Determine the correct quoting after a cast
+ *
+ * @param[in] existing_quote Exiting quotation type.
+ * @param[in] type Cast type.
+ * @param[in] enumv Enumeration values.
+ */
+static CC_HINT(always_inline) fr_token_t tmpl_cast_quote(fr_token_t existing_quote,
+ fr_type_t type, fr_dict_attr_t const *enumv,
+ char const *unescaped, size_t unescaped_len)
+{
+ if (!fr_type_is_string(type)) return T_BARE_WORD;
+
+ if (enumv && fr_dict_enum_by_name(enumv, unescaped, unescaped_len)) return T_BARE_WORD;
+
+ /*
+ * Leave the original quoting if it's
+ * single or double, else default to
+ * single quoting.
+ */
+ switch (existing_quote) {
+ case T_SINGLE_QUOTED_STRING:
+ case T_DOUBLE_QUOTED_STRING:
+ return existing_quote;
+
+ default:
+ return T_SINGLE_QUOTED_STRING;
+ }
+}
+
+
/** Convert #tmpl_t of type #TMPL_TYPE_UNRESOLVED or #TMPL_TYPE_DATA to #TMPL_TYPE_DATA of type specified
*
* @note Conversion is done in place.
char *unescaped = vpt->data.unescaped;
/*
- * Why do we pass a pointer to a temporary type
- * variable? Goddamn WiMAX.
+ * We're trying to convert an unresolved (bareword)
+ * tmpl to octets.
+ *
+ * tmpl_afrom_substr uses the 0x prefix as type
+ * inference, so if it was a hex string the tmpl
+ * type would not have fallen through to
+ * unresolved.
+ *
+ * That means if we're trying to resolve it here
+ * it's really a printable string, not a sequence
+ * of hexits, so we just want the binary
+ * representation of that string, and not the hex
+ * to bin conversion.
*/
- if (fr_value_box_from_str(vpt, &vpt->data.literal, type,
- enumv, unescaped, talloc_array_length(unescaped) - 1,
- '\0', false) < 0) return -1;
- talloc_free(unescaped);
+ if (fr_type_is_octets(type)) {
+ if (fr_value_box_memdup(vpt, &vpt->data.literal, enumv,
+ (uint8_t const *)unescaped, talloc_array_length(unescaped) - 1,
+ false) < 0) return -1;
+ } else {
+ if (fr_value_box_from_str(vpt, &vpt->data.literal, type,
+ enumv,
+ unescaped, talloc_array_length(unescaped) - 1,
+ NULL, false) < 0) return -1;
+ }
vpt->type = TMPL_TYPE_DATA;
+ vpt->quote = tmpl_cast_quote(vpt->quote, type, enumv,
+ unescaped, talloc_array_length(unescaped) - 1);
+ talloc_free(unescaped);
}
break;
case TMPL_TYPE_DATA:
{
- fr_value_box_t new;
-
if (type == tmpl_value_type(vpt)) return 0; /* noop */
- if (fr_value_box_cast(vpt, &new, type, enumv, &vpt->data.literal) < 0) return -1;
+ /*
+ * Enumerations aren't used when casting between
+ * data types. They're only used when processing
+ * unresolved tmpls.
+ *
+ * i.e. TMPL_TYPE_UNRESOLVED != TMPL_TYPE_DATA(FR_TYPE_STRING)
+ */
+ if (fr_value_box_cast_in_place(vpt, &vpt->data.literal, type, NULL) < 0) return -1;
- fr_value_box_clear(&vpt->data.literal);
- fr_value_box_copy(vpt, &vpt->data.literal, &new);
+ /*
+ * Strings get quoted, everything else is a bare
+ * word...
+ */
+ if (fr_type_is_string(type)) {
+ vpt->quote = T_SINGLE_QUOTED_STRING;
+ } else {
+ vpt->quote = T_BARE_WORD;
+ }
}
break;
default:
fr_assert(0);
}
-
- /*
- * Fixup quoting
- */
- switch (type) {
- case FR_TYPE_STRING:
- switch (vpt->quote) {
- case T_SINGLE_QUOTED_STRING:
- case T_DOUBLE_QUOTED_STRING:
- break;
-
- default:
- vpt->quote = T_SINGLE_QUOTED_STRING;
- break;
- }
- break;
-
- default:
- vpt->quote = T_BARE_WORD;
- break;
- }
-
TMPL_VERIFY(vpt);
return 0;
* Convert unresolved tmpls into literal string values.
*/
} else if (tmpl_is_unresolved(vpt)) {
- char *unescaped = vpt->data.unescaped; /* Copy the pointer before zeroing the union */
+ fr_type_t dst_type = vpt->cast;
- fr_value_box_init_null(&vpt->data.literal);
+ if (fr_type_is_null(dst_type)) dst_type = FR_TYPE_STRING; /* Default to strings */
- fr_value_box_bstrdup_buffer_shallow(NULL, &vpt->data.literal, NULL, unescaped, false);
+ if (tmpl_cast_in_place(vpt, dst_type, NULL) < 0) return -1;
- /*
- * Attempt to process the cast
- */
- if (vpt->cast != FR_TYPE_NULL) {
- ret = fr_value_box_cast_in_place(vpt, &vpt->data.literal, vpt->cast, NULL);
- if (ret < 0) goto done;
- }
- vpt->type = TMPL_TYPE_DATA;
TMPL_VERIFY(vpt);
}
-done:
return ret;
}
data_len -= 18;
MEM(pair_update_request(&vp, attr_soh_ms_machine_os_vendor) >= 0);
- fr_pair_value_from_str(vp, "Microsoft", -1, '\0', false);
+ fr_pair_value_from_str(vp, "Microsoft", sizeof("Microsoft") - 1, NULL, false);
MEM(pair_update_request(&vp, attr_soh_ms_machine_os_version) >= 0);
vp->vp_uint32 = soh_pull_be_32(p);
MEM(vp = fr_pair_afrom_da(ctx, da));
if (fr_pair_value_from_str(vp, (char *)fr_dbuff_current(out), fr_dbuff_remaining(out),
- '\0', true) < 0) {
+ NULL, true) < 0) {
RPWDEBUG3("Skipping: %s += \"%pV\"",
da->name, fr_box_strvalue_len((char *)fr_dbuff_current(out),
fr_dbuff_remaining(out)));
}
MEM(pair_update_request(&vp, attr_tls_psk_identity) >= 0);
- if (fr_pair_value_from_str(vp, identity, -1, '\0', true) < 0) {
+ if (fr_pair_value_from_str(vp, identity, strlen(identity), NULL, true) < 0) {
RPWDEBUG2("Failed parsing TLS PSK Identity");
talloc_free(vp);
return 0;
fr_value_box_list_init(&state->box);
MEM(box = fr_value_box_alloc(state->ctx, FR_TYPE_STRING, NULL, true));
if (fr_value_box_from_str(state->ctx, box, type, NULL,
- fr_sbuff_buff(&state->exec.stdout_buff),
- fr_sbuff_used(&state->exec.stdout_buff), 0, true) < 0) {
+ fr_sbuff_start(&state->exec.stdout_buff),
+ fr_sbuff_used(&state->exec.stdout_buff),
+ NULL, true) < 0) {
talloc_free(box);
*p_result = RLM_MODULE_FAIL;
return UNLANG_ACTION_CALCULATE_RESULT;
break;
case FR_TYPE_OCTETS:
- if (in_vb->vb_length > sizeof(uint64_t)) goto error;
+ if (in_vb->vb_length > sizeof(uint64_t)) {
+ fr_strerror_printf("Expected octets length <= %zu, got %zu", sizeof(uint64_t), in_vb->vb_length);
+ goto error;
+ }
if (in_vb->vb_length > sizeof(uint32_t)) {
fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT64, NULL);
- break;
+ } else if (in_vb->vb_length > sizeof(uint16_t)) {
+ fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT32, NULL);
+ } else if (in_vb->vb_length > sizeof(uint8_t)) {
+ fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT16, NULL);
+ } else {
+ fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT8, NULL);
}
- fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT32, NULL);
break;
case FR_TYPE_IPV4_ADDR:
fr_value_box_t data;
type = FR_TYPE_STRING;
- if (fr_value_box_from_str(ctx, &data, type, NULL, child,
- talloc_array_length(child) - 1, '"', false) < 0) {
+ if (fr_value_box_from_str(ctx, &data, type, NULL,
+ child, talloc_array_length(child) - 1,
+ &fr_value_unescape_double, false) < 0) {
talloc_free(child);
return NULL;
}
return 0;
}
- if (fr_pair_value_from_str(vp, expanded, -1, '"', true) < 0){
+ if (fr_pair_value_from_str(vp, expanded, strlen(expanded), &fr_value_unescape_double, true) < 0){
talloc_free(expanded);
return -2;
}
type = da->type;
if (fr_value_box_from_str(fixup, &value, type, NULL,
- fixup->value, talloc_array_length(fixup->value) - 1, '\0', false) < 0) {
- fr_strerror_printf_push("Invalid VALUE for Attribute '%s' at %s[%d]",
+ fixup->value, talloc_array_length(fixup->value) - 1,
+ NULL, false) < 0) {
+ fr_strerror_printf_push("Invalid VALUE '%pV' for attribute '%s' at %s[%d]",
+ fr_box_strvalue_buffer(fixup->value),
da->name,
fr_cwd_strip(fixup->common.filename), fixup->common.line);
return -1;
break;
}
- if (fr_value_box_from_str(NULL, &value, da->type, NULL, argv[2], -1, '\0', false) < 0) {
- fr_strerror_printf_push("Invalid VALUE for Attribute '%s'", da->name);
+ if (fr_value_box_from_str(NULL, &value, da->type, NULL,
+ argv[2], strlen(argv[2]),
+ NULL, false) < 0) {
+ fr_strerror_printf_push("Invalid VALUE '%s' for attribute '%s' of data type '%s'",
+ argv[2],
+ da->name,
+ fr_table_str_by_value(fr_value_box_type_table, da->type, "<INVALID>"));
return -1;
}
/*
* Parse the value.
*/
- if (fr_value_box_from_str(NULL, &value, parent->type, NULL, argv[2], -1, '\0', false) < 0) {
+ if (fr_value_box_from_str(NULL, &value, parent->type, NULL, argv[2], strlen(argv[2]), NULL, false) < 0) {
fr_strerror_printf_push("Invalid value for STRUCT \"%s\"", argv[2]);
return -1;
}
* Lookup by name
*/
case FR_SBUFF_PARSE_ERROR_NOT_FOUND:
+ case FR_SBUFF_PARSE_ERROR_TRAILING:
{
fr_dict_attr_err_t our_err;
ssize_t slen;
break;
default:
- fr_strerror_printf("Invalid OID component \"%.*s\"",
+ fr_strerror_printf("Invalid OID component (%s) \"%.*s\"",
+ fr_table_str_by_value(sbuff_parse_error_table, sberr, "<INVALID>"),
(int)fr_sbuff_remaining(in), fr_sbuff_current(in));
if (err) *err = FR_DICT_ATTR_PARSE_ERROR;
return -fr_sbuff_marker_release_behind(&start);
return NULL;
}
- if (fr_pair_value_from_str(vp, value, -1, '"', false) < 0) {
+ if (fr_pair_value_from_str(vp, value, strlen(value), &fr_value_unescape_double, false) < 0) {
talloc_free(vp);
return NULL;
}
* We probably want to fix fr_pair_value_from_str to accept
* octets as values for any attribute.
*/
- if (value && (fr_pair_value_from_str(vp, value, -1, '\"', true) < 0)) {
+ if (value && (fr_pair_value_from_str(vp, value, strlen(value), &fr_value_unescape_double, true) < 0)) {
talloc_free(vp);
return NULL;
}
* don't know. So just mark it
* as such to be safe.
*/
- } else if (fr_pair_value_from_str(vp, raw.r_opand, -1, '"', true) < 0) {
+ } else if (fr_pair_value_from_str(vp, raw.r_opand, strlen(raw.r_opand),
+ fr_value_unescape_by_quote[raw.quote], true) < 0) {
talloc_free(vp);
goto error;
}
*
* This isn't perfect, but it allows simple fuzzing of the parsers for all of the data types.
*/
-static ssize_t util_decode_proto(TALLOC_CTX *ctx, UNUSED fr_pair_list_t *out, uint8_t const *data, size_t data_len, UNUSED void *proto_ctx)
+static ssize_t util_decode_proto(TALLOC_CTX *ctx, UNUSED fr_pair_list_t *out, uint8_t const *data, size_t data_len,
+ UNUSED void *proto_ctx)
{
ssize_t rcode;
fr_type_t type;
/*
- * Some things in value_box_from_str() don't yet respect
+ * Some things in fr_value_box_from_substr() don't yet respect
* data_len. This means that if there's no zero
* termination, we _know_ there will be buffer over-runs.
*/
- rcode = fr_value_box_from_str(box, box, type, NULL, (char const *) copy, data_len - 1, 0, true);
+ rcode = fr_value_box_from_str(box, box, type, NULL,
+ (char const *)copy, data_len - 1,
+ NULL, true);
talloc_free(box);
return rcode;
}
* @param[in] vp to assign value to.
* @param[in] value string to convert. Binary safe for variable
* length values if len is provided.
- * @param[in] inlen may be < 0 in which case strlen(len) is used
- * to determine length, else inlen should be the
- * length of the string or sub string to parse.
- * @param[in] quote character used set unescape mode. @see fr_value_str_unescape.
+ * @param[in] inlen The length of the input string.
+ * @param[in] uerules used to perform unescaping.
* @param[in] tainted Whether the value came from a trusted source.
* @return
* - 0 on success.
* - -1 on failure.
*/
-int fr_pair_value_from_str(fr_pair_t *vp, char const *value, ssize_t inlen, char quote, bool tainted)
+int fr_pair_value_from_str(fr_pair_t *vp, char const *value, size_t inlen,
+ fr_sbuff_unescape_rules_t const *uerules, bool tainted)
{
/*
* This is not yet supported because the rest of the APIs
* We presume that the input data is from a double quoted
* string, and needs unescaping
*/
- if (fr_value_box_from_str(vp, &vp->data, vp->da->type, vp->da, value, inlen, quote, tainted) < 0) return -1;
+ if (fr_value_box_from_str(vp, &vp->data, vp->da->type, vp->da,
+ value, inlen,
+ uerules,
+ tainted) < 0) return -1;
vp->type = VT_DATA;
PAIR_VERIFY(vp);
* @{
*/
int fr_pair_value_from_str(fr_pair_t *vp,
- char const *value, ssize_t len, char quote, bool tainted) CC_HINT(nonnull);
+ char const *value, size_t len, fr_sbuff_unescape_rules_t const *erules,
+ bool tainted) CC_HINT(nonnull(1,2));
int fr_pair_value_strdup(fr_pair_t *vp, char const *src, bool tainted) CC_HINT(nonnull);
goto error;
}
- if (fr_pair_value_from_str(vp, value, -1, '"', false) < 0) goto error;
+ if (fr_pair_value_from_str(vp, value, strlen(value), &fr_value_unescape_double, false) < 0) goto error;
vp->op = (op == 0) ? T_OP_EQ : op;
return vp;
* We probably want to fix fr_pair_value_from_str to accept
* octets as values for any attribute.
*/
- if (value && (fr_pair_value_from_str(vp, value, -1, '\"', false) < 0)) {
+ if (value && (fr_pair_value_from_str(vp, value, strlen(value), &fr_value_unescape_double, false) < 0)) {
talloc_free(vp);
return NULL;
}
talloc_free(vp);
goto error;
}
-
- /*
- * Parse it ourselves. The RHS
- * might NOT be tainted, but we
- * don't know. So just mark it
- * as such to be safe.
- */
- } else if (fr_pair_value_from_str(vp, raw.r_opand, -1, '"', false) < 0) {
+ } else if (fr_pair_value_from_str(vp, raw.r_opand, strlen(raw.r_opand),
+ fr_value_unescape_by_quote[quote], false) < 0) {
talloc_free(vp);
goto error;
}
TEST_CHECK(vp != NULL);
if (!vp) return;
- TEST_CHECK(fr_pair_value_from_str(vp, test_string, strlen(test_string), '"', false) == 0);
+ TEST_CHECK(fr_pair_value_from_str(vp, test_string, strlen(test_string), &fr_value_unescape_double, false) == 0);
TEST_CASE("Validating PAIR_VERIFY()");
PAIR_VERIFY(vp);
PAIR_VERIFY(vp);
TEST_CASE("Convert 'test_string' value to attribute value using fr_pair_value_from_str()");
- TEST_CHECK(fr_pair_value_from_str(vp, test_string, strlen(test_string), '"', false) == 0);
+ TEST_CHECK(fr_pair_value_from_str(vp, test_string, strlen(test_string), &fr_value_unescape_double, false) == 0);
TEST_CASE("Validating PAIR_VERIFY()");
PAIR_VERIFY(vp);
* @param op the operator
* @param value the value to parse
* @param value_len length of the value string
- * @param quote the quotation character for the value string.
+ * @param uerules used for unescaping.
* @return
* - fr_pair_t* on success
* - NULL on error
static fr_pair_t *fr_pair_afrom_fields(TALLOC_CTX *ctx, fr_dict_attr_t const *da,
fr_token_t op,
char const *value, size_t value_len,
- char quote)
+ fr_sbuff_unescape_rules_t const *uerules)
{
fr_pair_t *vp;
vp->op = op;
- if (fr_pair_value_from_str(vp, value, value_len, quote, false) < 0) {
+ if (fr_pair_value_from_str(vp, value, value_len, uerules, false) < 0) {
talloc_free(vp);
return NULL;
}
return -(p - start);
}
- vp = fr_pair_afrom_fields(pair_ctx->ctx, da, op, value, value_len, quote);
+ vp = fr_pair_afrom_fields(pair_ctx->ctx, da, op, value, value_len, fr_value_unescape_by_char[(uint8_t)quote]);
if (!vp) return -(in - start);
fr_pair_append(pair_ctx->list, vp);
*/
#define FILL_OR_GOTO_DONE(_out, _in, _len) if (fr_sbuff_move(_out, _in, _len) < (size_t)(_len)) goto done
-/** Constrain end pointer to prevent advancing more than the amount the called specified
+/** Constrain end pointer to prevent advancing more than the amount the caller specified
*
* @param[in] _sbuff to constrain.
* @param[in] _max maximum amount to advance.
p = fr_sbuff_current(&our_in);
end = CONSTRAINED_END(&our_in, len, fr_sbuff_used_total(&our_in));
+ if (p == end) break;
+
if (escape_chr == '\0') {
while ((p < end) && !fr_sbuff_terminal_search(in, p, idx, tt, needle_len)) p++;
} else {
* @param[in] _max_char Maximum digits that can be used to represent an integer.
* Can't use stringify because of width modifiers like 'u'
* used in <stdint.h>.
+ * @param[in] _base to use.
*/
-#define SBUFF_PARSE_INT_DEF(_name, _type, _min, _max, _max_char) \
+#define SBUFF_PARSE_INT_DEF(_name, _type, _min, _max, _max_char, _base) \
fr_slen_t fr_sbuff_out_##_name(fr_sbuff_parse_error_t *err, _type *out, fr_sbuff_t *in, bool no_trailing) \
{ \
char buff[_max_char + 1]; \
if (err) *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND; \
return -1; \
} \
- num = strtoll(buff, &end, 10); \
+ errno = 0; \
+ num = strtoll(buff, &end, _base); \
if (end == buff) { \
if (err) *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND; \
return -1; \
} \
- if ((num > (_max)) || ((errno == EINVAL) && (num == LLONG_MAX))) { \
+ if ((num > (_max)) || ((errno == EINVAL) && (num == 0)) || ((errno == ERANGE) && (num == LLONG_MAX))) { \
if (err) *err = FR_SBUFF_PARSE_ERROR_NUM_OVERFLOW; \
*out = (_type)(_max); \
return -1; \
+ } else if ((num < (_min)) || ((errno == ERANGE) && (num == LLONG_MIN))) { \
+ if (err) *err = FR_SBUFF_PARSE_ERROR_NUM_UNDERFLOW; \
+ *out = (_type)(_min); \
+ return -1; \
} else if (no_trailing && (((a_end = in->p + (end - buff)) + 1) < in->end)) { \
- if (isdigit(*a_end)) { \
+ if (isdigit(*a_end) || (((_base > 10) || (_base == 0)) && ((tolower(*a_end) >= 'a') && (tolower(*a_end) <= 'f')))) { \
if (err) *err = FR_SBUFF_PARSE_ERROR_TRAILING; \
*out = (_type)(_max); \
return fr_sbuff_error(&our_in); \
} \
*out = (_type)(num); \
- } else if (num < (_min) || ((errno == EINVAL) && (num == LLONG_MIN))) { \
- if (err) *err = FR_SBUFF_PARSE_ERROR_NUM_UNDERFLOW; \
- *out = (_type)(_min); \
- return -1; \
} else { \
if (err) *err = FR_SBUFF_PARSE_OK; \
*out = (_type)(num); \
return fr_sbuff_advance(in, end - buff); /* Advance by the length strtoll gives us */ \
}
-SBUFF_PARSE_INT_DEF(int8, int8_t, INT8_MIN, INT8_MAX, 4)
-SBUFF_PARSE_INT_DEF(int16, int16_t, INT16_MIN, INT16_MAX, 6)
-SBUFF_PARSE_INT_DEF(int32, int32_t, INT32_MIN, INT32_MAX, 11)
-SBUFF_PARSE_INT_DEF(int64, int64_t, INT64_MIN, INT64_MAX, 20)
-SBUFF_PARSE_INT_DEF(ssize, ssize_t, SSIZE_MIN, SSIZE_MAX, 20)
+SBUFF_PARSE_INT_DEF(int8, int8_t, INT8_MIN, INT8_MAX, 4, 0)
+SBUFF_PARSE_INT_DEF(int16, int16_t, INT16_MIN, INT16_MAX, 6, 0)
+SBUFF_PARSE_INT_DEF(int32, int32_t, INT32_MIN, INT32_MAX, 11, 0)
+SBUFF_PARSE_INT_DEF(int64, int64_t, INT64_MIN, INT64_MAX, 20, 0)
+SBUFF_PARSE_INT_DEF(ssize, ssize_t, SSIZE_MIN, SSIZE_MAX, 20, 0)
/** Used to define a number parsing functions for singed integers
*
* @param[in] _max_char Maximum digits that can be used to represent an integer.
* Can't use stringify because of width modifiers like 'u'
* used in <stdint.h>.
- * @param[in] _base of the number being parsed, 8, 10, 18 etc...
+ * @param[in] _base of the number being parsed, 8, 10, 16 etc...
*/
#define SBUFF_PARSE_UINT_DEF(_name, _type, _max, _max_char, _base) \
fr_slen_t fr_sbuff_out_##_name(fr_sbuff_parse_error_t *err, _type *out, fr_sbuff_t *in, bool no_trailing) \
if (err) *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND; \
return -1; \
} \
+ if (buff[0] == '-') { \
+ if (err) *err = FR_SBUFF_PARSE_ERROR_NUM_UNDERFLOW; \
+ return -1; \
+ } \
+ errno = 0; \
num = strtoull(buff, &end, _base); \
if (end == buff) { \
if (err) *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND; \
return -1; \
} \
- if ((num > (_max)) || ((errno == EINVAL) && (num == ULLONG_MAX))) { \
+ if ((num > (_max)) || ((errno == EINVAL) && (num == 0)) || ((errno == ERANGE) && (num == ULLONG_MAX))) { \
if (err) *err = FR_SBUFF_PARSE_ERROR_NUM_OVERFLOW; \
*out = (_type)(_max); \
return -1; \
} else if (no_trailing && (((a_end = in->p + (end - buff)) + 1) < in->end)) { \
- if (isdigit(*a_end) || ((_base > 10) && ((tolower(*a_end) >= 'a') && (tolower(*a_end) <= 'f')))) { \
+ if (isdigit(*a_end) || (((_base > 10) || (_base == 0)) && ((tolower(*a_end) >= 'a') && (tolower(*a_end) <= 'f')))) { \
if (err) *err = FR_SBUFF_PARSE_ERROR_TRAILING; \
*out = (_type)(_max); \
return fr_sbuff_error(&our_in); \
return fr_sbuff_advance(in, end - buff); /* Advance by the length strtoull gives us */ \
}
-SBUFF_PARSE_UINT_DEF(uint8, uint8_t, UINT8_MAX, 3, 10)
-SBUFF_PARSE_UINT_DEF(uint16, uint16_t, UINT16_MAX, 4, 10)
-SBUFF_PARSE_UINT_DEF(uint32, uint32_t, UINT32_MAX, 10, 10)
-SBUFF_PARSE_UINT_DEF(uint64, uint64_t, UINT64_MAX, 19, 10)
-SBUFF_PARSE_UINT_DEF(size, size_t, SIZE_MAX, 19, 10)
+/* max chars here is the octal string value with prefix */
+SBUFF_PARSE_UINT_DEF(uint8, uint8_t, UINT8_MAX, 4, 0)
+SBUFF_PARSE_UINT_DEF(uint16, uint16_t, UINT16_MAX, 7, 0)
+SBUFF_PARSE_UINT_DEF(uint32, uint32_t, UINT32_MAX, 12, 0)
+SBUFF_PARSE_UINT_DEF(uint64, uint64_t, UINT64_MAX, 23, 0)
+SBUFF_PARSE_UINT_DEF(size, size_t, SIZE_MAX, 23, 0)
+
+SBUFF_PARSE_UINT_DEF(uint8_dec, uint8_t, UINT8_MAX, 3, 0)
+SBUFF_PARSE_UINT_DEF(uint16_dec, uint16_t, UINT16_MAX, 4, 0)
+SBUFF_PARSE_UINT_DEF(uint32_dec, uint32_t, UINT32_MAX, 10, 0)
+SBUFF_PARSE_UINT_DEF(uint64_dec, uint64_t, UINT64_MAX, 19, 0)
+SBUFF_PARSE_UINT_DEF(size_dec, size_t, SIZE_MAX, 19, 0)
+
SBUFF_PARSE_UINT_DEF(uint8_oct, uint8_t, UINT8_MAX, 3, 8)
SBUFF_PARSE_UINT_DEF(uint16_oct, uint16_t, UINT16_MAX, 6, 8)
if (err) *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND; \
return -1; \
} \
+ errno = 0; \
res = _func(buff, &end); \
if (errno == ERANGE) { \
if (res > 0) { \
*
* @private
*/
-#define _FR_SBUFF(_sbuff_or_marker, _start, _current, _end, _adv_parent) \
+#define _FR_SBUFF(_sbuff_or_marker, _start, _current, _end, _extend, _adv_parent) \
((fr_sbuff_t){ \
.buff = fr_sbuff_buff(_sbuff_or_marker), \
.start = (_start), \
.is_const = fr_sbuff_ptr(_sbuff_or_marker)->is_const, \
.adv_parent = (_adv_parent), \
.shifted = fr_sbuff_ptr(_sbuff_or_marker)->shifted, \
- .extend = fr_sbuff_ptr(_sbuff_or_marker)->extend, \
+ .extend = (_extend), \
.uctx = fr_sbuff_ptr(_sbuff_or_marker)->uctx, \
.parent = fr_sbuff_ptr(_sbuff_or_marker) \
})
fr_sbuff_current(_sbuff_or_marker), \
fr_sbuff_current(_sbuff_or_marker), \
fr_sbuff_end(_sbuff_or_marker), \
+ fr_sbuff_ptr(_sbuff_or_marker)->extend, \
0x00)
/** Create a new sbuff pointing to the same underlying buffer
fr_sbuff_start(_sbuff_or_marker), \
fr_sbuff_current(_sbuff_or_marker), \
fr_sbuff_end(_sbuff_or_marker), \
+ fr_sbuff_ptr(_sbuff_or_marker)->extend, \
0x00)
/** Create a new sbuff pointing to the same underlying buffer
* - Parent will _NOT_ be advanced by operations on its child.
* - Child will have its `start` pointer set to the `start` pointer of the parent.
* - Child will have its `end` pointer set to the `p` pointer of the parent.
+ * - Child will not extend parent.
*
* @param[in] _sbuff_or_marker to make an ephemeral copy of.
*/
fr_sbuff_start(_sbuff_or_marker), \
fr_sbuff_start(_sbuff_or_marker), \
fr_sbuff_current(_sbuff_or_marker), \
+ NULL, \
0x00)
/** Create a new sbuff pointing to the same underlying buffer
fr_sbuff_current(_sbuff_or_marker), \
fr_sbuff_current(_sbuff_or_marker), \
fr_sbuff_end(_sbuff_or_marker), \
+ fr_sbuff_ptr(_sbuff_or_marker)->extend, \
0x01)
/** Create a new sbuff pointing to the same underlying buffer
fr_sbuff_start(_sbuff_or_marker), \
fr_sbuff_current(_sbuff_or_marker), \
fr_sbuff_end(_sbuff_or_marker), \
+ fr_sbuff_ptr(_sbuff_or_marker)->extend, \
0x01)
/** Creates a compound literal to pass into functions which accept a sbuff
fr_slen_t fr_sbuff_out_uint64(fr_sbuff_parse_error_t *err, uint64_t *out, fr_sbuff_t *sbuff, bool no_trailing);
fr_slen_t fr_sbuff_out_size(fr_sbuff_parse_error_t *err, size_t *out, fr_sbuff_t *sbuff, bool no_trailing);
+fr_slen_t fr_sbuff_out_uint8_dec(fr_sbuff_parse_error_t *err, uint8_t *out, fr_sbuff_t *sbuff, bool no_trailing);
+fr_slen_t fr_sbuff_out_uint16_dec(fr_sbuff_parse_error_t *err, uint16_t *out, fr_sbuff_t *sbuff, bool no_trailing);
+fr_slen_t fr_sbuff_out_uint32_dec(fr_sbuff_parse_error_t *err, uint32_t *out, fr_sbuff_t *sbuff, bool no_trailing);
+fr_slen_t fr_sbuff_out_uint64_dec(fr_sbuff_parse_error_t *err, uint64_t *out, fr_sbuff_t *sbuff, bool no_trailing);
+fr_slen_t fr_sbuff_out_size_dec(fr_sbuff_parse_error_t *err, size_t *out, fr_sbuff_t *sbuff, bool no_trailing);
+
fr_slen_t fr_sbuff_out_uint8_oct(fr_sbuff_parse_error_t *err, uint8_t *out, fr_sbuff_t *sbuff, bool no_trailing);
fr_slen_t fr_sbuff_out_uint16_oct(fr_sbuff_parse_error_t *err, uint16_t *out, fr_sbuff_t *sbuff, bool no_trailing);
fr_slen_t fr_sbuff_out_uint32_oct(fr_sbuff_parse_error_t *err, uint32_t *out, fr_sbuff_t *sbuff, bool no_trailing);
* - PRESENTATION format is what we print to the screen, and what we get from the user, databases
* and configuration files.
* - #fr_value_box_aprint is used to convert from INTERNAL to PRESENTATION format.
- * - #fr_value_box_from_str is used to convert from PRESENTATION to INTERNAL format.
+ * - #fr_value_box_from_substr is used to convert from PRESENTATION to INTERNAL format.
*
* @copyright 2014-2017 The FreeRADIUS server project
* @copyright 2017 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
switch (src->type) {
case FR_TYPE_STRING:
return fr_value_box_from_str(ctx, dst, dst_type, dst_enumv,
- src->vb_strvalue, src->vb_length, '\0', src->tainted);
+ src->vb_strvalue, src->vb_length,
+ NULL, src->tainted);
default:
break;
switch (src->type) {
case FR_TYPE_STRING:
return fr_value_box_from_str(ctx, dst, dst_type, dst_enumv,
- src->vb_strvalue, src->vb_length, '\0', src->tainted);
+ src->vb_strvalue, src->vb_length,
+ NULL, src->tainted);
default:
break;
switch (src->type) {
case FR_TYPE_STRING:
return fr_value_box_from_str(ctx, dst, dst_type, dst_enumv,
- src->vb_strvalue, src->vb_length, '\0', src->tainted);
+ src->vb_strvalue, src->vb_length,
+ NULL, src->tainted);
default:
break;
switch (src->type) {
case FR_TYPE_STRING:
return fr_value_box_from_str(ctx, dst, dst_type, dst_enumv,
- src->vb_strvalue, src->vb_length, '\0', src->tainted);
+ src->vb_strvalue, src->vb_length,
+ NULL, src->tainted);
default:
break;
switch (src->type) {
case FR_TYPE_STRING:
return fr_value_box_from_str(ctx, dst, dst_type, dst_enumv,
- src->vb_strvalue, src->vb_length, '\0', src->tainted);
+ src->vb_strvalue, src->vb_length,
+ NULL, src->tainted);
case FR_TYPE_OCTETS:
return fr_value_box_fixed_size_from_octets(dst, dst_type, dst_enumv, src);
switch (src->type) {
case FR_TYPE_STRING:
return fr_value_box_from_str(ctx, dst, dst_type, dst_enumv,
- src->vb_strvalue, src->vb_length, '\0', src->tainted);
+ src->vb_strvalue, src->vb_length,
+ NULL, src->tainted);
case FR_TYPE_OCTETS:
return fr_value_box_fixed_size_from_octets(dst, dst_type, dst_enumv, src);
switch (src->type) {
case FR_TYPE_STRING:
return fr_value_box_from_str(ctx, dst, dst_type, dst_enumv,
- src->vb_strvalue, src->vb_length, '\0', src->tainted);
+ src->vb_strvalue, src->vb_length,
+ NULL, src->tainted);
case FR_TYPE_OCTETS:
return fr_value_box_fixed_size_from_octets(dst, dst_type, dst_enumv, src);
*
* This should be the canonical function used to convert between INTERNAL data formats.
*
- * If you want to convert from PRESENTATION format, use #fr_value_box_from_str.
+ * If you want to convert from PRESENTATION format, use #fr_value_box_from_substr.
*
* @note src and dst must not be the same box. We do not support casting in place.
*
* Deserialise a fr_value_box_t
*/
if (src->type == FR_TYPE_STRING) return fr_value_box_from_str(ctx, dst, dst_type, dst_enumv,
- src->vb_strvalue,
- src->vb_length, '\0', src->tainted);
+ src->vb_strvalue, src->vb_length,
+ NULL, src->tainted);
if (src->type == FR_TYPE_OCTETS) {
fr_value_box_t tmp;
*
* This should be the canonical function used to convert between INTERNAL data formats.
*
- * If you want to convert from PRESENTATION format, use #fr_value_box_from_str.
+ * If you want to convert from PRESENTATION format, use #fr_value_box_from_substr.
*
* @param ctx to allocate buffers in (usually the same as dst)
* @param vb to cast.
* @param[out] dst where to write parsed value.
* @param[in] dst_type type of integer to convert string to.
* @param[in] in String to convert to integer.
+ * @param[in] tainted Whether the value came from a trusted source.
* @return
- * - 0 on success.
- * - -1 on parse error.
+ * - >= 0 on success (number of bytes parsed).
+ * - < 0 on error (where the parse error ocurred).
*/
-static int fr_value_box_from_integer_str(fr_value_box_t *dst, fr_type_t dst_type, char const *in)
+static inline CC_HINT(always_inline)
+fr_slen_t fr_value_box_from_numeric_substr(fr_value_box_t *dst, fr_type_t dst_type,
+ fr_dict_attr_t const *dst_enumv,
+ fr_sbuff_t *in, bool tainted)
{
- uint64_t uinteger = 0;
- int64_t sinteger = 0;
- char *p = NULL;
-
- switch (dst_type) {
- case FR_TYPE_UINT8:
- case FR_TYPE_UINT16:
- case FR_TYPE_UINT32:
- case FR_TYPE_UINT64:
- fr_skip_whitespace(in);
-
- if (*in == '-') {
- fr_strerror_printf("Invalid negative value \"%s\" for unsigned integer", in);
- return -1;
- }
-
- /*
- * fr_strtoull checks for overflows and calls
- * fr_strerror_printf to set an error.
- */
- if (fr_strtoull(&uinteger, &p, in) < 0) return -1;
- if (*p != '\0') {
- fr_strerror_printf("Invalid integer value \"%s\"", in);
-
- return -1;
- }
- if (errno == ERANGE) {
-
+ fr_slen_t slen;
+ fr_sbuff_parse_error_t err;
- return -1;
- }
- break;
-
- case FR_TYPE_INT8:
- case FR_TYPE_INT16:
- case FR_TYPE_INT32:
- case FR_TYPE_INT64:
- /*
- * fr_strtoll checks for overflows and calls
- * fr_strerror_printf to set an error.
- */
- if (fr_strtoll(&sinteger, &p, in) < 0) return -1;
- if (*p != '\0') {
- fr_strerror_printf("Invalid integer value \"%s\"", in);
-
- return -1;
- }
- break;
-
- default:
- fr_assert_fail(NULL);
- return -1;
- }
-
-#define IN_RANGE_UNSIGNED(_type) \
- do { \
- if (uinteger > _type ## _MAX) { \
- fr_strerror_printf("Value %" PRIu64 " is invalid for type %s (must be in range " \
- "0...%" PRIu64 ")", \
- uinteger, fr_table_str_by_value(fr_value_box_type_table, dst_type, "<INVALID>"), \
- (uint64_t) _type ## _MAX); \
- return -1; \
- } \
- } while (0)
-
-#define IN_RANGE_SIGNED(_type) \
- do { \
- if ((sinteger > _type ## _MAX) || (sinteger < _type ## _MIN)) { \
- fr_strerror_printf("Value %" PRId64 " is invalid for type %s (must be in range " \
- "%" PRId64 "...%" PRId64 ")", \
- sinteger, fr_table_str_by_value(fr_value_box_type_table, dst_type, "<INVALID>"), \
- (int64_t) _type ## _MIN, (int64_t) _type ## _MAX); \
- return -1; \
- } \
- } while (0)
+ fr_value_box_init(dst, dst_type, dst_enumv, tainted);
switch (dst_type) {
case FR_TYPE_UINT8:
- IN_RANGE_UNSIGNED(UINT8);
- dst->vb_uint8 = (uint8_t)uinteger;
+ slen = fr_sbuff_out(&err, &dst->vb_uint8, in);
break;
case FR_TYPE_UINT16:
- IN_RANGE_UNSIGNED(UINT16);
- dst->vb_uint16 = (uint16_t)uinteger;
+ slen = fr_sbuff_out(&err, &dst->vb_uint16, in);
break;
case FR_TYPE_UINT32:
- IN_RANGE_UNSIGNED(UINT32);
- dst->vb_uint32 = (uint32_t)uinteger;
+ slen = fr_sbuff_out(&err, &dst->vb_uint32, in);
break;
case FR_TYPE_UINT64:
- /* IN_RANGE_UNSIGNED doesn't work here */
- dst->vb_uint64 = (uint64_t)uinteger;
+ slen = fr_sbuff_out(&err, &dst->vb_uint64, in);
break;
case FR_TYPE_INT8:
- IN_RANGE_SIGNED(INT8);
- dst->vb_int8 = (int8_t)sinteger;
+ slen = fr_sbuff_out(&err, &dst->vb_int8, in);
break;
case FR_TYPE_INT16:
- IN_RANGE_SIGNED(INT16);
- dst->vb_int16 = (int16_t)sinteger;
+ slen = fr_sbuff_out(&err, &dst->vb_int16, in);
break;
case FR_TYPE_INT32:
- IN_RANGE_SIGNED(INT32);
- dst->vb_int32 = (int32_t)sinteger;
+ slen = fr_sbuff_out(&err, &dst->vb_int32, in);
break;
case FR_TYPE_INT64:
- /* IN_RANGE_SIGNED doesn't work here */
- dst->vb_int64 = (int64_t)sinteger;
+ slen = fr_sbuff_out(&err, &dst->vb_int64, in);
+ break;
+
+ case FR_TYPE_SIZE:
+ slen = fr_sbuff_out(&err, &dst->vb_size, in);
+ break;
+
+ case FR_TYPE_FLOAT32:
+ slen = fr_sbuff_out(&err, &dst->vb_float32, in);
+ break;
+
+ case FR_TYPE_FLOAT64:
+ slen = fr_sbuff_out(&err, &dst->vb_float64, in);
break;
default:
return -1;
}
- return 0;
+ if (slen < 0) fr_sbuff_parse_error_to_strerror(err);
+
+ return slen;
}
/** Convert string value to a fr_value_box_t type
* @param[out] dst where to write parsed value.
* @param[in,out] dst_type of value data to create/dst_type of value created.
* @param[in] dst_enumv fr_dict_attr_t with string names for uint32 values.
- * @param[in] in String to convert. Binary safe for variable length values
- * if len is provided.
- * @param[in] inlen may be < 0 in which case strlen(len) is used to determine
- * length, else inlen should be the length of the string or
- * sub string to parse.
- * @param[in] quote character used set unescape mode. @see fr_value_str_unescape.
+ * @param[in] in sbuff to read data from.
+ * @param[in] rules unescape and termination rules.
* @param[in] tainted Whether the value came from a trusted source.
* @return
- * - 0 on success.
- * - -1 on parse error.
+ * - >0 on success.
+ * - <= 0 on parse error.
*/
-int fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst,
- fr_type_t dst_type, fr_dict_attr_t const *dst_enumv,
- char const *in, ssize_t inlen, char quote, bool tainted)
+ssize_t fr_value_box_from_substr(TALLOC_CTX *ctx, fr_value_box_t *dst,
+ fr_type_t dst_type, fr_dict_attr_t const *dst_enumv,
+ fr_sbuff_t *in, fr_sbuff_parse_rules_t const *rules, bool tainted)
{
- size_t len;
- ssize_t ret;
- char buffer[256];
+ static fr_sbuff_parse_rules_t default_rules;
+ fr_sbuff_t *unescaped = NULL;
+ fr_sbuff_t our_in = FR_SBUFF(in);
+ ssize_t ret;
+ char buffer[256];
if (!fr_cond_assert(dst_type != FR_TYPE_NULL)) return -1;
- len = (inlen < 0) ? strlen(in) : (size_t)inlen;
-
- /*
- * If the data is tainted, then the data should never be
- * quoted.
- */
- fr_assert(!tainted || (quote == 0));
+ if (!rules) rules = &default_rules;
/*
* Set size for all fixed length attributes.
* Lookup any names before continuing
*/
if (dst_enumv) {
- char *tmp = NULL;
- char *name;
- size_t name_len;
+ size_t name_len;
fr_dict_enum_value_t *enumv;
- if (len > (sizeof(buffer) - 1)) {
- name_len = fr_value_str_aunescape(NULL, &tmp,
- &FR_SBUFF_IN(in, len), SIZE_MAX, quote);
- name = tmp;
- } else {
- name_len = fr_value_str_unescape(&FR_SBUFF_OUT(buffer, sizeof(buffer)),
- &FR_SBUFF_IN(in, len), SIZE_MAX, quote);
- name = buffer;
+ /*
+ * Create a thread-local extensible buffer to
+ * store unescaped data.
+ *
+ * This is created once per-thread (the first time
+ * this function is called), and freed when the
+ * thread exits.
+ */
+ FR_SBUFF_TALLOC_THREAD_LOCAL(&unescaped, 256, 4096);
+
+ name_len = fr_sbuff_out_unescape_until(unescaped, &our_in, SIZE_MAX,
+ rules->terminals, rules->escapes);
+ if (!name_len) {
+ fr_sbuff_set_to_start(&our_in);
+ goto parse; /* Zero length name can't match enum */
}
- fr_assert(name);
- enumv = fr_dict_enum_by_name(dst_enumv, name, name_len);
- if (tmp) talloc_free(tmp);
- if (!enumv) goto parse;
+ enumv = fr_dict_enum_by_name(dst_enumv, fr_sbuff_start(unescaped), fr_sbuff_used(unescaped));
+ if (!enumv) {
+ fr_sbuff_set_to_start(&our_in);
+ goto parse; /* No enumeration matches escaped string */
+ }
- fr_value_box_copy(ctx, dst, enumv->value);
- dst->enumv = dst_enumv;
+ /*
+ * dst_type may not match enumv type
+ */
+ if (fr_value_box_cast(ctx, dst, dst_type, dst_enumv, enumv->value) < 0) return -1;
- return 0;
+ return fr_sbuff_set(in, &our_in);
}
parse:
*/
switch (dst_type) {
case FR_TYPE_STRING:
- {
- char *buff;
+ /*
+ * We've not unescaped the string yet, produce an unescaped version
+ */
+ if (!dst_enumv) {
+ char *buff;
- ret = fr_value_str_aunescape(ctx, &buff, &FR_SBUFF_IN(in, len), SIZE_MAX, quote);
- talloc_get_type_abort(buff, char);
- dst->vb_strvalue = buff;
- }
- goto finish;
+ fr_sbuff_out_aunescape_until(ctx, &buff, &our_in, SIZE_MAX, rules->terminals, rules->escapes);
+ fr_value_box_bstrdup_buffer_shallow(NULL, dst, dst_enumv, buff, tainted);
+ /*
+ * We already have an unescaped version, just use that
+ */
+ } else {
+ fr_value_box_bstrndup(ctx, dst, dst_enumv,
+ fr_sbuff_start(unescaped), fr_sbuff_used(unescaped), tainted);
+ }
+ return fr_sbuff_set(in, &our_in);
/* raw octets: 0x01020304... */
case FR_TYPE_OCTETS:
{
- uint8_t *p;
+ fr_sbuff_marker_t hex_start;
+ size_t hex_len;
+ uint8_t *bin_buff;
/*
- * No 0x prefix, just copy verbatim.
+ * If there's escape sequences that need to be processed
+ * or the string doesn't start with 0x, then assume this
+ * is literal data, not hex encoded data.
*/
- if ((len < 2) || (strncasecmp(in, "0x", 2) != 0)) {
- dst->vb_octets = talloc_memdup(ctx, (uint8_t const *)in, len);
- talloc_set_type(dst->vb_octets, uint8_t);
- ret = len;
- goto finish;
+ if (rules->escapes || !fr_sbuff_adv_past_strcase_literal(&our_in, "0x")) {
+ if (!dst_enumv) {
+ char *buff;
+ uint8_t *bin;
+
+ fr_sbuff_out_aunescape_until(ctx, &buff, &our_in, SIZE_MAX,
+ rules->terminals, rules->escapes);
+
+ bin = talloc_realloc(ctx, buff, uint8_t, talloc_array_length(buff) - 1);
+ if (unlikely(!bin)) {
+ fr_strerror_const("Failed trimming string buffer");
+ talloc_free(buff);
+ return -1;
+ }
+
+ fr_value_box_memdup_buffer_shallow(NULL, dst, dst_enumv, bin, tainted);
+ /*
+ * We already have an unescaped version, just use that
+ */
+ } else {
+ fr_value_box_memdup(ctx, dst, dst_enumv,
+ (uint8_t *)fr_sbuff_start(unescaped),
+ fr_sbuff_used(unescaped), tainted);
+ }
+ return fr_sbuff_set(in, &our_in);
}
- len -= 2;
+ fr_sbuff_marker(&hex_start, &our_in); /* Record where the hexits start */
/*
- * Invalid.
+ * Find the end of the hex sequence.
+ *
+ * We don't technically need to do this, fr_base16_decode
+ * will find the end on its own.
+ *
+ * We do this so we can alloc the correct sized
+ * output buffer.
*/
- if ((len & 0x01) != 0) {
- fr_strerror_printf("Length of hex string is not even, got %zu bytes", len);
- return -1;
+ hex_len = fr_sbuff_adv_past_allowed(&our_in, SIZE_MAX, sbuff_char_class_hex, rules->terminals);
+ if (hex_len == 0) {
+ if (fr_value_box_memdup(ctx, dst, dst_enumv, (uint8_t[]){ 0x00 }, 0, tainted) < 0) return -1;
+ return fr_sbuff_set(in, &our_in);
}
- ret = len >> 1;
- p = talloc_array(ctx, uint8_t, ret);
- if (fr_base16_decode(NULL, &FR_DBUFF_TMP(p, ret), &FR_SBUFF_IN(in + 2, len), false) != (ssize_t)ret) {
- talloc_free(p);
- fr_strerror_const("Invalid hex data");
- return -1;
+ if ((hex_len & 0x01) != 0) {
+ fr_strerror_printf("Length of hex string is not even, got %zu bytes", hex_len);
+ return fr_sbuff_error(&our_in);
}
- dst->vb_octets = p;
+ /*
+ * Pre-allocate the bin buff and initialise the box
+ */
+ if (fr_value_box_mem_alloc(ctx, &bin_buff, dst, dst_enumv, (hex_len >> 1), tainted) < 0) return -1;
+
+ /*
+ * Reset to the start of the hex string
+ */
+ fr_sbuff_set(&our_in, &hex_start);
+
+ if (unlikely(fr_base16_decode(NULL,
+ &FR_DBUFF_TMP(bin_buff, hex_len),
+ &our_in, false) != (ssize_t)ret) < 0) {
+ talloc_free(bin_buff);
+ return fr_sbuff_error(&our_in);
+ }
+
+ return fr_sbuff_set(in, &our_in);
}
- goto finish;
case FR_TYPE_IPV4_ADDR:
{
fr_ipaddr_t addr;
- if (fr_inet_pton4(&addr, in, inlen, fr_hostname_lookups, false, true) < 0) return -1;
+ if (fr_inet_pton4(&addr, fr_sbuff_current(in), fr_sbuff_remaining(in),
+ fr_hostname_lookups, false, true) < 0) return -1;
/*
* We allow v4 addresses to have a /32 suffix as some databases (PostgreSQL)
goto finish;
case FR_TYPE_IPV4_PREFIX:
- if (fr_inet_pton4(&dst->vb_ip, in, inlen, fr_hostname_lookups, false, true) < 0) return -1;
+ if (fr_inet_pton4(&dst->vb_ip, fr_sbuff_current(in), fr_sbuff_remaining(in),
+ fr_hostname_lookups, false, true) < 0) return -1;
goto finish;
case FR_TYPE_IPV6_ADDR:
{
fr_ipaddr_t addr;
- if (fr_inet_pton6(&addr, in, inlen, fr_hostname_lookups, false, true) < 0) return -1;
+ if (fr_inet_pton6(&addr, fr_sbuff_current(in), fr_sbuff_remaining(in),
+ fr_hostname_lookups, false, true) < 0) return -1;
/*
* We allow v6 addresses to have a /128 suffix as some databases (PostgreSQL)
goto finish;
case FR_TYPE_IPV6_PREFIX:
- if (fr_inet_pton6(&dst->vb_ip, in, inlen, fr_hostname_lookups, false, true) < 0) return -1;
+ if (fr_inet_pton6(&dst->vb_ip, fr_sbuff_current(in), fr_sbuff_remaining(in),
+ fr_hostname_lookups, false, true) < 0) return -1;
goto finish;
+ case FR_TYPE_UINT8:
+ case FR_TYPE_UINT16:
+ case FR_TYPE_UINT32:
+ case FR_TYPE_UINT64:
+ case FR_TYPE_INT8:
+ case FR_TYPE_INT16:
+ case FR_TYPE_INT32:
+ case FR_TYPE_INT64:
+ case FR_TYPE_FLOAT32:
+ case FR_TYPE_FLOAT64:
+ return fr_value_box_from_numeric_substr(dst, dst_type, dst_enumv, in, tainted);
+
+ case FR_TYPE_BOOL:
+ {
+ fr_slen_t slen;
+
+ fr_value_box_init(dst, dst_type, dst_enumv, tainted);
+
+ /*
+ * Quoted boolean values are "yes", "no", "true", "false"
+ */
+ slen = fr_sbuff_out(NULL, &dst->vb_bool, in);
+ if (slen >= 0) return slen;
+
+ /*
+ * For barewords we also allow 0 for false and any other
+ * integer value for true.
+ */
+ if ((slen < 0) && (!rules->escapes)) {
+ int64_t stmp;
+ uint64_t utmp;
+
+ slen = fr_sbuff_out(NULL, &stmp, in);
+ if (slen >= 0) {
+ dst->vb_bool = (stmp != 0);
+ return slen;
+ }
+
+ slen = fr_sbuff_out(NULL, &utmp, in);
+ if (slen >= 0) {
+ dst->vb_bool = (utmp != 0);
+ return slen;
+ }
+ }
+
+ fr_strerror_const("Invalid boolean value. Accepted values are "
+ "\"yes\", \"no\", \"true\", \"false\" or any unquoted integer");
+
+ return slen; /* Just whatever the last error offset was */
+ }
+
+ case FR_TYPE_NULL:
+ if (!rules->escapes && fr_sbuff_adv_past_str_literal(in, "NULL")) {
+ fr_value_box_init(dst, dst_type, dst_enumv, tainted);
+ return fr_sbuff_set(in, &our_in);
+ }
+
+ fr_strerror_const("String value was not NULL");
+ return -1;
+
/*
* Dealt with below
*/
default:
break;
-
- case FR_TYPE_STRUCTURAL:
- case FR_TYPE_MAX:
- case FR_TYPE_NULL:
- fr_strerror_printf("Invalid dst_type %s",
- fr_table_str_by_value(fr_value_box_type_table, dst_type, "<INVALID>"));
- return -1;
}
/*
* It's a fixed size src->dst_type, copy to a temporary buffer and
* \0 terminate if insize >= 0.
*/
- if (inlen > 0) {
- if (len >= sizeof(buffer)) {
+ if (fr_sbuff_remaining(in) > 0) {
+ if (fr_sbuff_remaining(in) >= sizeof(buffer)) {
fr_strerror_const("Temporary buffer too small");
return -1;
}
- memcpy(buffer, in, inlen);
- buffer[inlen] = '\0';
- in = buffer;
+ memcpy(buffer, fr_sbuff_current(in), fr_sbuff_remaining(in));
+ buffer[fr_sbuff_remaining(in)] = '\0';
}
switch (dst_type) {
- case FR_TYPE_IPV4_ADDR:
- case FR_TYPE_IPV4_PREFIX:
- case FR_TYPE_IPV6_ADDR:
- case FR_TYPE_IPV6_PREFIX:
- break;
-
- case FR_TYPE_UINT8:
- case FR_TYPE_UINT16:
- case FR_TYPE_UINT32:
- case FR_TYPE_UINT64:
- case FR_TYPE_INT8:
- case FR_TYPE_INT16:
- case FR_TYPE_INT32:
- case FR_TYPE_INT64:
- if (fr_value_box_from_integer_str(dst, dst_type, in) < 0) return -1;
- break;
-
case FR_TYPE_SIZE:
- if (fr_size_from_str(&dst->datum.size, in) < 0) return -1;
+ if (fr_size_from_str(&dst->datum.size, buffer) < 0) return -1;
break;
case FR_TYPE_TIME_DELTA:
if (dst_enumv) {
- if (fr_time_delta_from_str(&dst->datum.time_delta, in, dst_enumv->flags.flag_time_res) < 0) return -1;
+ if (fr_time_delta_from_str(&dst->datum.time_delta, buffer, dst_enumv->flags.flag_time_res) < 0) return -1;
} else {
- if (fr_time_delta_from_str(&dst->datum.time_delta, in, FR_TIME_RES_SEC) < 0) return -1;
+ if (fr_time_delta_from_str(&dst->datum.time_delta, buffer, FR_TIME_RES_SEC) < 0) return -1;
}
break;
- case FR_TYPE_FLOAT32:
- {
- float f;
-
- if (sscanf(in, "%f", &f) != 1) {
- fr_strerror_printf("Failed parsing \"%s\" as a float32", in);
- return -1;
- }
- dst->vb_float32 = f;
- }
- break;
-
- case FR_TYPE_FLOAT64:
- {
- double d;
-
- if (sscanf(in, "%lf", &d) != 1) {
- fr_strerror_printf("Failed parsing \"%s\" as a float64", in);
- return -1;
- }
- dst->vb_float64 = d;
- }
- break;
-
case FR_TYPE_DATE:
{
if (dst_enumv) {
- if (fr_unix_time_from_str(&dst->vb_date, in, dst_enumv->flags.flag_time_res) < 0) return -1;
+ if (fr_unix_time_from_str(&dst->vb_date, buffer, dst_enumv->flags.flag_time_res) < 0) return -1;
} else {
- if (fr_unix_time_from_str(&dst->vb_date, in, FR_TIME_RES_SEC) < 0) return -1;
+ if (fr_unix_time_from_str(&dst->vb_date, buffer, FR_TIME_RES_SEC) < 0) return -1;
}
dst->enumv = dst_enumv;
break;
case FR_TYPE_IFID:
- if (fr_inet_ifid_pton((void *) dst->vb_ifid, in) == NULL) {
- fr_strerror_printf("Failed to parse interface-id string \"%s\"", in);
+ if (fr_inet_ifid_pton((void *) dst->vb_ifid, buffer) == NULL) {
+ fr_strerror_printf("Failed to parse interface-id string \"%s\"", buffer);
return -1;
}
break;
* 8-byte number, and then the lower bytes of
* that get copied to the ethernet address.
*/
- if (is_integer(in)) {
- uint64_t lvalue = htonll(atoll(in));
+ if (is_integer(buffer)) {
+ uint64_t lvalue = htonll(atoll(buffer));
memcpy(dst->vb_ether, ((uint8_t *) &lvalue) + 2, sizeof(dst->vb_ether));
break;
}
- cp = in;
+ cp = buffer;
while (*cp) {
if (cp[1] == ':') {
c1 = hextab;
c1 = c2 = NULL;
}
if (!c1 || !c2 || (p_len >= sizeof(dst->vb_ether))) {
- fr_strerror_printf("failed to parse Ethernet address \"%s\"", in);
+ fr_strerror_printf("failed to parse Ethernet address \"%s\"", buffer);
return -1;
}
dst->vb_ether[p_len] = ((c1-hextab)<<4) + (c2-hextab);
* and attribute as the original.
*/
case FR_TYPE_COMBO_IP_ADDR:
- if (fr_inet_pton(&dst->vb_ip, in, inlen, AF_UNSPEC, fr_hostname_lookups, true) < 0) return -1;
+ if (fr_inet_pton(&dst->vb_ip, buffer, strlen(buffer), AF_UNSPEC, fr_hostname_lookups, true) < 0) return -1;
switch (dst->vb_ip.af) {
case AF_INET:
ret = network_max_size(FR_TYPE_IPV4_ADDR);
break;
case FR_TYPE_COMBO_IP_PREFIX:
- if (fr_inet_pton(&dst->vb_ip, in, inlen, AF_UNSPEC, fr_hostname_lookups, true) < 0) return -1;
+ if (fr_inet_pton(&dst->vb_ip, buffer, strlen(buffer), AF_UNSPEC, fr_hostname_lookups, true) < 0) return -1;
switch (dst->vb_ip.af) {
case AF_INET:
ret = network_max_size(FR_TYPE_IPV4_PREFIX);
}
break;
- case FR_TYPE_BOOL:
- if ((strcmp(in, "yes") == 0) || (strcmp(in, "true") == 0) || (strcmp(in, "1") == 0)) {
- dst->datum.boolean = true;
- } else if ((strcmp(in, "no") == 0) || (strcmp(in, "false") == 0) || (strcmp(in, "0") == 0)) {
- dst->datum.boolean = false;
- } else {
- fr_strerror_printf("\"%s\" is not a valid boolean value", in);
- return -1;
- }
- break;
-
- case FR_TYPE_NULL:
- if ((quote == '\0') && (strcmp(in, "NULL") == 0)) goto finish;
- fr_strerror_const("String value was not NULL");
- break;
-
- case FR_TYPE_VALUE_BOX:
- case FR_TYPE_VARIABLE_SIZE: /* Should have been dealt with above */
- case FR_TYPE_STRUCTURAL: /* Listed again to suppress compiler warnings */
- case FR_TYPE_VOID:
- case FR_TYPE_MAX:
-
+ default:
fr_strerror_printf("Unknown attribute dst_type %d", dst_type);
return -1;
}
return 0;
}
+ssize_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst,
+ fr_type_t dst_type, fr_dict_attr_t const *dst_enumv,
+ char const *in, size_t inlen,
+ fr_sbuff_unescape_rules_t const *erules, bool tainted)
+{
+ ssize_t slen;
+ fr_sbuff_parse_rules_t prules = { .escapes = erules };
+
+ slen = fr_value_box_from_substr(ctx, dst, dst_type, dst_enumv, &FR_SBUFF_IN(in, inlen), &prules, tainted);
+ if (slen <= 0) return slen;
+
+ if (slen != (ssize_t)inlen) {
+ fr_strerror_printf("%zu bytes of trailing data after string value \"%pV\"",
+ inlen - slen,
+ fr_box_strvalue_len(in + slen, inlen - slen));
+ return (slen - inlen) - 1;
+ }
+
+ return slen;
+}
+
/** Print one boxed value to a string
*
* This function should primarily be used when a #fr_value_box_t is being
*
* @{
*/
-int fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst,
+ssize_t fr_value_box_from_substr(TALLOC_CTX *ctx, fr_value_box_t *dst,
+ fr_type_t dst_type, fr_dict_attr_t const *dst_enumv,
+ fr_sbuff_t *in, fr_sbuff_parse_rules_t const *rules, bool tainted)
+ CC_HINT(nonnull(2,5));
+
+ssize_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst,
fr_type_t dst_type, fr_dict_attr_t const *dst_enumv,
- char const *src, ssize_t src_len, char quote, bool tainted)
+ char const *in, size_t inlen,
+ fr_sbuff_unescape_rules_t const *erules, bool tainted)
CC_HINT(nonnull(2,5));
/** @} */
char const *value = cf_pair_value(cp);
MEM(vp = fr_pair_afrom_da(ctx, da));
- if (fr_pair_value_from_str(vp, value, talloc_array_length(value) - 1, '\0', false) < 0) {
+ if (fr_pair_value_from_str(vp, value, talloc_array_length(value) - 1, NULL, false) < 0) {
RWDEBUG("Failed parsing value \"%pV\" for attribute %s: %s", fr_box_strvalue(value),
tmpl_da(map->lhs)->name, fr_strerror());
talloc_free(vp);
sizeof(*e) + (inst->used_fields * sizeof(e->data[0]))));
talloc_set_type(e, rlm_csv_entry_t);
- e->key = talloc_zero(e, fr_value_box_t);
- if (fr_value_box_from_str(e->key, e->key, type, NULL, p, -1, 0, false) < 0) {
+ e->key = fr_value_box_alloc_null(e);
+ if (fr_value_box_from_str(e->key, e->key, type, NULL, p, strlen(p), NULL, false) < 0) {
cf_log_err(conf, "Failed parsing key field in file %s line %d - %s", inst->filename, lineno,
fr_strerror());
return false;
/*
* Set the last entry to use 'e'
*/
- e->key = talloc_zero(e, fr_value_box_t);
- if (fr_value_box_from_str(e->key, e->key, type, NULL, p, -1, 0, false) < 0) {
+ e->key = fr_value_box_alloc_null(e);
+ if (fr_value_box_from_str(e->key, e->key, type, NULL,
+ p, strlen(p), NULL, false) < 0) {
cf_log_err(conf, "Failed parsing key field in file %s line %d - %s", inst->filename, lineno,
fr_strerror());
fail:
fr_value_box_t box;
fr_type_t type = inst->field_types[i];
- if (fr_value_box_from_str(e, &box, type, NULL, p, -1, 0, false) < 0) {
+ if (fr_value_box_from_str(e, &box, type, NULL,
+ p, strlen(p), NULL, false) < 0) {
cf_log_err(conf, "Failed parsing field '%s' in file %s line %d - %s", inst->field_names[i],
inst->filename, lineno, fr_strerror());
goto fail;
vp = fr_pair_afrom_da(ctx, da);
fr_assert(vp);
- if (fr_pair_value_from_str(vp, str, talloc_array_length(str) - 1, '\0', true) < 0) {
+ if (fr_pair_value_from_str(vp, str, talloc_array_length(str) - 1, NULL, true) < 0) {
RPWDEBUG("Failed parsing value \"%pV\" for attribute %s", fr_box_strvalue_buffer(str),
tmpl_da(map->lhs)->name);
talloc_free(vp);
* Tell the request that it's a fake one.
*/
MEM(fr_pair_prepend_by_da(fake->request_ctx, &vp, &fake->request_pairs, attr_freeradius_proxied_to) >= 0);
- fr_pair_value_from_str(vp, "127.0.0.1", sizeof("127.0.0.1"), '\0', false);
+ fr_pair_value_from_str(vp, "127.0.0.1", sizeof("127.0.0.1") - 1, NULL, false);
/*
* If there's no User-Name in the stored data, look for
* Tell the request that it's a fake one.
*/
MEM(fr_pair_prepend_by_da(fake->request_ctx, &vp, &fake->request_pairs, attr_freeradius_proxied_to) >= 0);
- fr_pair_value_from_str(vp, "127.0.0.1", sizeof("127.0.0.1"), '\0', false);
+ fr_pair_value_from_str(vp, "127.0.0.1", sizeof("127.0.0.1") - 1, NULL, false);
if (t->username) {
vp = fr_pair_copy(fake->request_ctx, t->username);
/*
* Has to be of the correct data type.
*/
- if (fr_value_box_from_str(box, box, data_type, NULL, entry->name, -1, 0, false) < 0) {
+ if (fr_value_box_from_str(box, box, data_type, NULL,
+ entry->name, strlen(entry->name), NULL, false) < 0) {
ERROR("%s[%d] Failed parsing key %s - %s",
entry->filename, entry->lineno, entry->name, fr_strerror());
goto error;
info->argv[info->argc] = talloc_zero(info, fr_value_box_t);
ret = fr_value_box_from_str(info, info->argv[info->argc], type, NULL,
- state->token, state->token_len, 0, false);
+ state->token, state->token_len, NULL, false);
if (ret < 0) return ret;
info->argc++;
type = FR_TYPE_UINT32;
ret = fr_value_box_from_str(NULL, &box, type, NULL,
- state->token, state->token_len, 0, false);
+ state->token, state->token_len, NULL, false);
if (ret < 0) goto error;
/*
/*
* Add in the first value.
*/
- ret = fr_pair_value_from_str(vp, value, talloc_array_length(value) - 1, '\0', false);
+ ret = fr_pair_value_from_str(vp, value, talloc_array_length(value) - 1, NULL, false);
if (ret < 0) {
talloc_free(value);
return ret;
MEM(vp = fr_pair_afrom_da(parent, da));
- ret = fr_pair_value_from_str(vp, state->token, state->token_len, '\0', false);
+ ret = fr_pair_value_from_str(vp, state->token, state->token_len, NULL, false);
if (ret < 0) return ret;
vp->op = T_OP_EQ;
talloc_free(dst);
vp->op = op;
- if (fr_pair_value_from_str(vp, cval, -1, '\0', false) < 0) {
+ if (fr_pair_value_from_str(vp, cval, strlen(cval), NULL, false) < 0) {
REDEBUG("%s: %s %s %s failed", function_name, ckey, fr_table_str_by_value(fr_tokens_table, op, "="), cval);
} else {
DEBUG("%s: %s %s %s OK", function_name, ckey, fr_table_str_by_value(fr_tokens_table, op, "="), cval);
break;
default:
- if (fr_pair_value_from_str(vp, val, len, '\0', false) < 0) goto fail;
+ if (fr_pair_value_from_str(vp, val, len, NULL, false) < 0) goto fail;
}
PAIR_VERIFY(vp);
talloc_free(dst);
vp->op = op;
- if (fr_pair_value_from_str(vp, s2, -1, '\0', false) < 0) {
+ if (fr_pair_value_from_str(vp, s2, strlen(s2), NULL, false) < 0) {
DEBUG("%s - Failed: '%s.%s' %s '%s'", funcname, list_name, s1,
fr_table_str_by_value(fr_tokens_table, op, "="), s2);
} else {
return count;
}
- ret = fr_pair_value_from_str(vp, expanded, -1, '\0', true);
+ ret = fr_pair_value_from_str(vp, expanded, strlen(value), NULL, true);
TALLOC_FREE(expanded);
if (ret < 0) {
RWDEBUG("Incompatible value assignment, skipping");
* Buffer not always talloced, sometimes it's
* just a pointer to a field in a result struct.
*/
- if (fr_pair_value_from_str(vp, value, -1, '\0', true) < 0) {
+ if (fr_pair_value_from_str(vp, value, strlen(value), NULL, true) < 0) {
RPEDEBUG("Failed parsing value \"%pV\" for attribute %s",
fr_box_strvalue_buffer(value), tmpl_da(map->lhs)->name);
talloc_free(vp);
* specific to the SQL module.
*/
} else {
- if (fr_pair_value_from_str(vp, value, -1, '\0', true) < 0) {
+ if (fr_pair_value_from_str(vp, value, strlen(value), NULL, true) < 0) {
RPEDEBUG("Error parsing value");
talloc_free(vp);
* error out. If so, add it to the list.
*/
MEM(vp = fr_pair_afrom_da(request->reply_ctx, inst->allocated_address_da));
- if (fr_pair_value_from_str(vp, allocation, allocation_len, '\0', true) < 0) {
+ if (fr_pair_value_from_str(vp, allocation, allocation_len, NULL, true) < 0) {
DO_PART(alloc_commit);
RDEBUG2("Invalid IP number [%s] returned from instbase query.", allocation);
*
* And if that fails, just ignore it completely.
*/
- if (fr_pair_value_from_str(vp, (char const *) value, arg_end - value, 0, true) < 0) {
+ if (fr_pair_value_from_str(vp, (char const *) value, arg_end - value, NULL, true) < 0) {
fail:
talloc_free(vp);
if (da != parent) goto raw;
}
update request {
- &Tmp-Integer-3 := "%(sqldate:%{Tmp-String-3})"
+ &Tmp-String-0 := "%(sqldate:%{Tmp-String-3})"
}
-if (&Tmp-Integer-3 != 0) {
+if (&Tmp-String-0 != "") {
test_fail
}
test_fail
}
-success
\ No newline at end of file
+success
# Expected answer
#
Packet-Type == Access-Accept
-Called-Station-Id == '1 foo\n'
+Called-Station-Id == "1 foo\n"
Called-Station-Id == '1 bar'
-Called-Station-Id == '2 foo\n'
+Called-Station-Id == "2 foo\n"
Called-Station-Id == '2 bar'
-Called-Station-Id == '3 foo\n'
+Called-Station-Id == "3 foo\n"
Called-Station-Id == '3 bar'
-Called-Station-Id == '4 foo\n'
+Called-Station-Id == "4 foo\n"
Called-Station-Id == '4 bar'
update request {
- &Tmp-Integer-0 := "%{rand:-1}"
- &Tmp-Integer-1 := "%{rand:hello world}"
- &Tmp-Integer-2 := "%{rand:123}"
+ &Tmp-String-0 := "%{rand:-1}"
}
#
# Negative limit should have failed
#
-if (&Tmp-Integer-0 != 0) {
+if (&Tmp-String-0 != '') {
test_fail
}
+update request {
+ &Tmp-String-0 := "%{rand:hello world}"
+}
+
#
# Invalid limit should have failed
#
-if (&Tmp-Integer-1 != 0) {
+if (&Tmp-String-0 != 0) {
test_fail
}
+update request {
+ &Tmp-Integer-0 := "%{rand:123}"
+}
+
#
# Make sure random number is whithin limit
#
-if (&Tmp-Integer-2 < 0 || &Tmp-Integer-2 > 123) {
+if (&Tmp-Integer-0 < 0 || &Tmp-Integer-0 > 123) {
test_fail
}
# 8192 - 0x (2) - '' (2) there are unlikely to be any static buffers this big outside of the conffile parser
update request {
- &Tmp-Octets-0 := '0x\
+ &Tmp-Octets-0 := 0x\
d8abccb7834711af1de1812be2579febe946f5d7beef6fa5c7074c0cb917e9b91e23e14b016f27610097c16c0e0fad88176e077b24198c770746159\
05b8810d1c8b774d98889fa5c6027cde5e9c56dd4f7c48298c7713aeca5ba5dcfd506032ad05b1396f50e825b633d5a6af0dce6181b640287e03a65\
8734df46a86341556f28455b3f377313a5a2ac8c8267b8a5de559b95f9b493a68b9e0278485f9e3914d702b2b7b90ee85ff393461f197386d09b836\
5ccf45c5af9c05a47905ac8bfe55f9b912c3fca856abe7863f87392b6f6b0069e3d8412a056b1034aaac506bf2ca51760aa180c5f43a751beb06e88\
fc6afab4c5dd4aae4a2e6f0293c15278d557c2925acba90c73eeea09ebb95f0d469aa77ae983a0e69dacd55bd8a7e78a41df5227de35af05127fa3b\
a02f4a3ab98d75992d68a15d393387fe9ef01041569570ad6fe884764e55567311bcacfcffae76554dcfebfde607500f50ec85f589eaade607500f5\
-3ae4bcef324b9a3f47ba5c835b54a010ca42f3b9cc5368278c148c9b02ea8c4c9f244fd49f'
+3ae4bcef324b9a3f47ba5c835b54a010ca42f3b9cc5368278c148c9b02ea8c4c9f244fd49f
}
# Actual length of octet string is 4083 bytes
match true
condition <ipaddr>127.0.0.1/327 == 127.0.0.1
-match ERROR offset 8: Failed parsing value as type 'ipv4prefix'
+match ERROR offset 8: Failed parsing value as type 'ipaddr'
condition <ipaddr>127.0.0.1/32 == 127.0.0.1
match true
# uint8
#
value uint8 256
-match Value 256 is invalid for type uint8 (must be in range 0...255)
+match integer overflow
value uint8 -128
-match Invalid negative value "-128" for unsigned integer
+match integer underflow
value int8 128
-match Value 128 is invalid for type int8 (must be in range -128...127)
+match integer overflow
value int8 -128
match -128
value int8 -130
-match Value -130 is invalid for type int8 (must be in range -128...127)
+match integer underflow
value date Jan 1 1970 12:00:00 UTC
match Jan 1 1970 12:00:00 UTC
match ERROR offset 2: Invalid regex reference. Must be in range 0-32
xlat %{3a}
-match ERROR offset 2: Unexpected text after attribute reference
+match ERROR offset 2: Invalid regex reference. Must be in range 0-32
xlat %{-3}
match ERROR offset 2: Unresolved attributes not allowed in expansions here