* @param[in] list where the parsed fr_pair_ts will be appended.
* @param[in,out] token The last token we parsed
* @param[in] depth the nesting depth for FR_TYPE_GROUP
+ * @param[in,out] relative_vp for relative attributes
* @return
* - <= 0 on failure.
* - The number of bytes of name consumed on success.
*/
static ssize_t fr_pair_list_afrom_substr(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, char const *buffer,
- fr_pair_list_t *list, fr_token_t *token, int depth)
+ fr_pair_list_t *list, fr_token_t *token, int depth, fr_pair_t **relative_vp)
{
fr_pair_list_t tmp_list;
fr_pair_t *vp = NULL;
+ fr_pair_t *my_relative_vp;
char const *p, *q, *next;
fr_token_t quote, last_token = T_INVALID;
fr_pair_t_RAW raw;
fr_dict_attr_t const *internal = fr_dict_root(fr_dict_internal());
+ fr_pair_list_t *my_list;
+ TALLOC_CTX *my_ctx;
if (internal == parent) internal = NULL;
*/
if (strncmp(p, "raw.", 4) == 0) goto do_unknown;
- /*
- * Parse the name.
- */
- slen = fr_dict_attr_by_oid_substr(NULL, &da, parent,
- &FR_SBUFF_IN(p, strlen(p)), &bareword_terminals);
- if ((slen <= 0) && internal) {
- slen = fr_dict_attr_by_oid_substr(NULL, &da, internal,
+ if (*p == '.') {
+ p++;
+
+ if (!*relative_vp) {
+ fr_strerror_const("Relative attributes can only be used immediately after an attribute of type 'group'");
+ goto error;
+ }
+
+ slen = fr_dict_attr_by_oid_substr(NULL, &da, (*relative_vp)->da,
&FR_SBUFF_IN(p, strlen(p)), &bareword_terminals);
- }
- if (slen <= 0) {
- do_unknown:
- slen = fr_dict_unknown_afrom_oid_substr(ctx, NULL, &da_unknown, parent,
- &FR_SBUFF_IN(p, strlen(p)), &bareword_terminals);
+ if (slen <= 0) goto error;
+
+ my_list = &(*relative_vp)->vp_group;
+ my_ctx = *relative_vp;
+ } else {
+ /*
+ * Parse the name.
+ */
+ slen = fr_dict_attr_by_oid_substr(NULL, &da, parent,
+ &FR_SBUFF_IN(p, strlen(p)), &bareword_terminals);
+ if ((slen <= 0) && internal) {
+ slen = fr_dict_attr_by_oid_substr(NULL, &da, internal,
+ &FR_SBUFF_IN(p, strlen(p)), &bareword_terminals);
+ }
if (slen <= 0) {
- p += -slen;
+ do_unknown:
+ slen = fr_dict_unknown_afrom_oid_substr(ctx, NULL, &da_unknown, parent,
+ &FR_SBUFF_IN(p, strlen(p)), &bareword_terminals);
+ if (slen <= 0) {
+ p += -slen;
+
+ error:
+ fr_pair_list_free(&tmp_list);
+ *token = T_INVALID;
+ return -(p - buffer);
+ }
- error:
- fr_pair_list_free(&tmp_list);
- *token = T_INVALID;
- return -(p - buffer);
+ da = da_unknown;
}
- da = da_unknown;
+ my_list = &tmp_list;
+ my_ctx = ctx;
}
next = p + slen;
}
p++;
- vp = fr_pair_afrom_da(ctx, da);
+ vp = fr_pair_afrom_da(my_ctx, da);
if (!vp) goto error;
/*
parent = fr_dict_attr_ref(da);
if (!parent) parent = da;
- slen = fr_pair_list_afrom_substr(vp, vp->da, p, &vp->vp_group, &last_token, depth + 1);
+ /*
+ * Parse nested attributes, but the
+ * attributes here are relative to each
+ * other, and not to our parent relative VP.
+ */
+ my_relative_vp = NULL;
+ slen = fr_pair_list_afrom_substr(vp, vp->da, p, &vp->vp_group, &last_token, depth + 1, &my_relative_vp);
if (slen <= 0) {
talloc_free(vp);
goto error;
fr_skip_whitespace(p);
if (*p != '}') goto failed_group;
p++;
+
+ /*
+ * Cache which VP is now the one for
+ * relative references.
+ */
+ *relative_vp = vp;
break;
case FR_TYPE_LEAF:
* @todo - note that they will also be escaped,
* so we may need to fix that later.
*/
- vp = fr_pair_afrom_da(ctx, da);
+ vp = fr_pair_afrom_da(my_ctx, da);
if (!vp) goto error;
vp->op = raw.op;
fr_dict_unknown_free(&da);
fr_assert(vp != NULL);
- fr_pair_append(&tmp_list, vp);
+ fr_pair_append(my_list, vp);
+
+ /*
+ * If there's a relative VP, and it's not the one
+ * we just added above, and we're not adding this
+ * VP to the relative one, then nuke the relative
+ * VP.
+ */
+ if (*relative_vp && (vp != *relative_vp) && (my_ctx != *relative_vp)) {
+ *relative_vp = NULL;
+ }
/*
* Now look for EOL, hash, etc.
fr_token_t fr_pair_list_afrom_str(TALLOC_CTX *ctx, fr_dict_t const *dict, char const *buffer, fr_pair_list_t *list)
{
fr_token_t token;
+ fr_pair_t *relative_vp = NULL;
- (void) fr_pair_list_afrom_substr(ctx, fr_dict_root(dict), buffer, list, &token, 0);
+ (void) fr_pair_list_afrom_substr(ctx, fr_dict_root(dict), buffer, list, &token, 0, &relative_vp);
return token;
}
fr_token_t last_token = T_EOL;
bool found = false;
char buf[8192];
+ fr_pair_t *relative_vp = NULL;
while (fgets(buf, sizeof(buf), fp) != NULL) {
fr_pair_list_t tmp_list;
/*
* Call our internal function, instead of the public wrapper.
*/
- if (fr_pair_list_afrom_substr(ctx, fr_dict_root(dict), buf, &tmp_list, &last_token, 0) < 0) {
+ if (fr_pair_list_afrom_substr(ctx, fr_dict_root(dict), buf, &tmp_list, &last_token, 0, &relative_vp) < 0) {
goto fail;
}
+ /*
+ * @todo - rely on actually checking the syntax, and "OK" result, instead of guessing.
+ *
+ * The main issue is that it's OK to read no
+ * attributes on a particular line, but only if
+ * it's comments.
+ */
if (fr_dlist_empty(&tmp_list.head)) {
if (last_token == T_EOL) break;
+ /*
+ * This is allowed for relative attributes.
+ */
+ if (relative_vp && (last_token == T_COMMA)) {
+ found = true;
+ continue;
+ }
+
fail:
/*
* Didn't read anything, but the previous
Packet-Type = Access-Request
User-Name = "bob",
Digest-Response = "bdbeebb2da6adb6bca02599c2239e192"
-Digest-Attributes = { Realm = "biloxi.com", Nonce = "dcd98b7102dd2f0e8b11d0f600bfb0c093", Method = "INVITE", URI = "sip:bob@biloxi.com", Algorithm = "MD5", User-Name = "bob", QOP = "auth-int", Nonce-Count = "00000001", CNonce = "0a4f113b", Body-Digest = "c1ed018b8ec4a3b170c0921f5b564e48" }
-
-#Digest-Attributes.Realm = "biloxi.com",
-#Digest-Attributes.Nonce = "dcd98b7102dd2f0e8b11d0f600bfb0c093",
-#Digest-Attributes.Method = "INVITE",
-#Digest-Attributes.URI = "sip:bob@biloxi.com",
-#Digest-Attributes.Algorithm = "MD5",
-#Digest-Attributes.User-Name = "bob",
-#Digest-Attributes.QOP = "auth-int",
-#Digest-Attributes.Nonce-Count = "00000001",
-#Digest-Attributes.CNonce = "0a4f113b",
-#Digest-Attributes.Body-Digest = "c1ed018b8ec4a3b170c0921f5b564e48",
+Digest-Attributes = { Realm = "biloxi.com" },
+.Nonce = "dcd98b7102dd2f0e8b11d0f600bfb0c093",
+.Method = "INVITE",
+.URI = "sip:bob@biloxi.com",
+.Algorithm = "MD5",
+.User-Name = "bob",
+.QOP = "auth-int",
+.Nonce-Count = "00000001",
+.CNonce = "0a4f113b",
+.Body-Digest = "c1ed018b8ec4a3b170c0921f5b564e48"
Packet-Type == Access-Accept