> On Tue, Feb 21, 2017 at 6:32 PM, Yann Ylavic <[email protected]> wrote:
>>
>> Header set Client-SAN "expr=%{PeerExtList:2.5.29.17}"
This currently fails because list functions are not recognized in a
string context.
For now, lists can be either expressed with the syntax "{ <word>,
<word>, ... }", with <word> being itself something powerful, or
obtained from the mod_ssl's PeerExtList() function (grab anything from
a peer certificate).
For the latter case (or for future functions), it could be useful to
be able to work on such strings (e.g. with extracting regexes).
So I wonder if we could return the string elements separated by
something in the case of lists evaluated in a string context.
For example, the attached patch uses the seprator ", " (quite HTTP
field inspired), but it could be a json string or whatever...
We could also have an explicit tostring/tojson() function which would
stringify anything as argument.
Or yet more operators on lists, like list_empty(), list_first(),
list_last(), list_nth(), list_match(<list>, <regex>) (returning
another list of matching entries), ... you name it.
Working on anything from a certificates looks very useful at least.
WDYT?
Index: server/util_expr_eval.c
===================================================================
--- server/util_expr_eval.c (revision 1783852)
+++ server/util_expr_eval.c (working copy)
@@ -50,6 +50,9 @@ AP_IMPLEMENT_HOOK_RUN_FIRST(int, expr_lookup, (ap_
static const char *ap_expr_eval_string_func(ap_expr_eval_ctx_t *ctx,
const ap_expr_t *info,
const ap_expr_t *args);
+static apr_array_header_t *ap_expr_eval_list_func(ap_expr_eval_ctx_t *ctx,
+ const ap_expr_t *info,
+ const ap_expr_t *args);
static const char *ap_expr_eval_re_backref(ap_expr_eval_ctx_t *ctx,
unsigned int n);
static const char *ap_expr_eval_var(ap_expr_eval_ctx_t *ctx,
@@ -80,6 +83,8 @@ static int inc_rec(ap_expr_eval_ctx_t *ctx)
return 1;
}
+#define AP_EXPR_MAX_LIST_STRINGS 500
+
static const char *ap_expr_eval_word(ap_expr_eval_ctx_t *ctx,
const ap_expr_t *node)
{
@@ -161,6 +166,35 @@ static const char *ap_expr_eval_word(ap_expr_eval_
result = ap_expr_eval_string_func(ctx, info, args);
break;
}
+ case op_ListFuncCall: {
+ const ap_expr_t *info = node->node_arg1;
+ const ap_expr_t *args = node->node_arg2;
+ apr_array_header_t *array = ap_expr_eval_list_func(ctx, info, args);
+ if (array && array->nelts > 0) {
+ struct iovec *vec;
+ int n = array->nelts, i = 0;
+ /* sanity check */
+ if (n > AP_EXPR_MAX_LIST_STRINGS) {
+ n = AP_EXPR_MAX_LIST_STRINGS;
+ }
+ /* all entries (but last) separated by ", " */
+ n = (n * 2) - 1;
+ vec = apr_palloc(ctx->p, n * sizeof(struct iovec));
+ for (;;) {
+ const char *s = APR_ARRAY_IDX(array, i, const char *);
+ vec[i].iov_base = (void *)s;
+ vec[i].iov_len = strlen(s);
+ if (++i >= n) {
+ break;
+ }
+ vec[i].iov_base = (void *)", ";
+ vec[i].iov_len = 2;
+ ++i;
+ }
+ result = apr_pstrcatv(ctx->p, vec, n, NULL);
+ }
+ break;
+ }
case op_RegexBackref: {
const unsigned int *np = node->node_arg1;
result = ap_expr_eval_re_backref(ctx, *np);
@@ -213,6 +247,19 @@ static const char *ap_expr_eval_string_func(ap_exp
return (*func)(ctx, data, ap_expr_eval_word(ctx, arg));
}
+static apr_array_header_t *ap_expr_eval_list_func(ap_expr_eval_ctx_t *ctx,
+ const ap_expr_t *info,
+ const ap_expr_t *arg)
+{
+ ap_expr_list_func_t *func = (ap_expr_list_func_t *)info->node_arg1;
+ const void *data = info->node_arg2;
+
+ AP_DEBUG_ASSERT(info->node_op == op_ListFuncInfo);
+ AP_DEBUG_ASSERT(func != NULL);
+ AP_DEBUG_ASSERT(data != NULL);
+ return (*func)(ctx, data, ap_expr_eval_word(ctx, arg));
+}
+
static int intstrcmp(const char *s1, const char *s2)
{
apr_int64_t i1 = apr_atoi64(s1);
@@ -268,13 +315,8 @@ static int ap_expr_eval_comp(ap_expr_eval_ctx_t *c
}
else if (e2->node_op == op_ListFuncCall) {
const ap_expr_t *info = e2->node_arg1;
- const ap_expr_t *arg = e2->node_arg2;
- ap_expr_list_func_t *func = (ap_expr_list_func_t *)info->node_arg1;
- apr_array_header_t *haystack;
-
- AP_DEBUG_ASSERT(func != NULL);
- AP_DEBUG_ASSERT(info->node_op == op_ListFuncInfo);
- haystack = (*func)(ctx, info->node_arg2, ap_expr_eval_word(ctx, arg));
+ const ap_expr_t *args = e2->node_arg2;
+ apr_array_header_t *haystack = ap_expr_eval_list_func(ctx, info, args);
if (haystack == NULL) {
return 0;
}
@@ -474,8 +516,19 @@ ap_expr_t *ap_expr_str_func_make(const char *name,
ap_expr_parse_ctx_t *ctx)
{
ap_expr_t *info = ap_expr_info_make(AP_EXPR_FUNC_STRING, name, ctx, arg);
- if (!info)
- return NULL;
+ if (!info) {
+ /* We know how to make a string result from a list, so if no string
+ * function is found, try to find a list function.
+ */
+ const char *saved_error = ctx->error2;
+ ctx->error2 = NULL;
+ info = ap_expr_list_func_make(name, arg, ctx);
+ if (!info) {
+ /* Didn't work, restore previous error */
+ ctx->error2 = saved_error;
+ }
+ return info;
+ }
info->node_op = op_StringFuncInfo;
return ap_expr_make(op_StringFuncCall, info, arg, ctx);