Initial commit
Change-Id: I6a4444e3c193dae437cd7929f4c39aba7b749efa
diff --git a/extensions/acl_wl/CMakeLists.txt b/extensions/acl_wl/CMakeLists.txt
new file mode 100644
index 0000000..bd51150
--- /dev/null
+++ b/extensions/acl_wl/CMakeLists.txt
@@ -0,0 +1,29 @@
+# The acl_wl extension
+PROJECT("Access Control / White List" C)
+
+# Parser files
+BISON_FILE(aw_conf.y)
+FLEX_FILE(aw_conf.l)
+SET_SOURCE_FILES_PROPERTIES(lex.aw_conf.c aw_conf.tab.c PROPERTIES COMPILE_FLAGS "-I ${CMAKE_CURRENT_SOURCE_DIR}")
+
+# List of source files
+SET( ACL_WL_SRC
+ acl_wl.h
+ acl_wl.c
+ aw_tree.c
+ lex.aw_conf.c
+ aw_conf.tab.c
+ aw_conf.tab.h
+)
+
+# Compile as a module
+FD_ADD_EXTENSION(acl_wl ${ACL_WL_SRC})
+
+
+####
+## INSTALL section ##
+
+# We install with the daemon component because it is a base feature.
+INSTALL(TARGETS acl_wl
+ LIBRARY DESTINATION ${INSTALL_EXTENSIONS_SUFFIX}
+ COMPONENT freeDiameter-daemon)
diff --git a/extensions/acl_wl/acl_wl.c b/extensions/acl_wl/acl_wl.c
new file mode 100644
index 0000000..c28ddd9
--- /dev/null
+++ b/extensions/acl_wl/acl_wl.c
@@ -0,0 +1,117 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* Copyright (c) 2011, WIDE Project and NICT *
+* All rights reserved. *
+* *
+* Redistribution and use of this software in source and binary forms, with or without modification, are *
+* permitted provided that the following conditions are met: *
+* *
+* * Redistributions of source code must retain the above *
+* copyright notice, this list of conditions and the *
+* following disclaimer. *
+* *
+* * Redistributions in binary form must reproduce the above *
+* copyright notice, this list of conditions and the *
+* following disclaimer in the documentation and/or other *
+* materials provided with the distribution. *
+* *
+* * Neither the name of the WIDE Project or NICT nor the *
+* names of its contributors may be used to endorse or *
+* promote products derived from this software without *
+* specific prior written permission of WIDE Project and *
+* NICT. *
+* *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
+*********************************************************************************************************/
+
+/*
+ * Whitelist extension for freeDiameter.
+ */
+
+#include "acl_wl.h"
+
+/* The validator function */
+static int aw_validate(struct peer_info * info, int * auth, int (**cb2)(struct peer_info *))
+{
+ int res;
+
+ TRACE_ENTRY("%p %p %p", info, auth, cb2);
+
+ CHECK_PARAMS(info && auth && cb2);
+
+ /* We don't use the second callback */
+ *cb2 = NULL;
+
+ /* Default to unknown result */
+ *auth = 0;
+
+ /* Now search the peer in our tree */
+ CHECK_FCT( aw_tree_lookup(info->pi_diamid, &res) );
+ if (res < 0) {
+ /* The peer is not whitelisted */
+ return 0;
+ }
+
+ /* We found the peer in the tree, now check the status */
+
+ /* First, if TLS is already in place, just accept */
+ if (info->runtime.pir_cert_list) {
+ *auth = 1;
+ return 0;
+ }
+
+ /* Now, if we did not specify any flag, reject */
+ if (res == 0) {
+ TRACE_DEBUG(INFO, "Peer '%s' rejected, only TLS-protected connection is whitelisted.", info->pi_diamid);
+ /* We don't actually set *auth = -1, leave space for a further extension to validate the peer */
+ return 0;
+ }
+
+ /* Otherwise, just set the configured flags for the peer, and authorize it */
+ *auth = 1;
+
+ /* Save information about the security mechanism to use after CER/CEA exchange */
+ if ((res & PI_SEC_NONE) && (res & PI_SEC_TLS_OLD))
+ res = PI_SEC_NONE; /* If we authorized it, we must have an IPsec tunnel setup, no need for TLS in this case */
+
+ info->config.pic_flags.sec = res;
+ return 0;
+}
+
+/* entry point */
+static int aw_entry(char * conffile)
+{
+ TRACE_ENTRY("%p", conffile);
+ CHECK_PARAMS(conffile);
+
+ /* Parse configuration file */
+ CHECK_FCT( aw_conf_handle(conffile) );
+
+ TRACE_DEBUG(INFO, "Extension ACL_wl initialized with configuration: '%s'", conffile);
+ if (TRACE_BOOL(ANNOYING)) {
+ aw_tree_dump();
+ }
+
+ /* Register the validator function */
+ CHECK_FCT( fd_peer_validate_register ( aw_validate ) );
+
+ return 0;
+}
+
+/* Unload */
+void fd_ext_fini(void)
+{
+ /* Destroy the tree */
+ aw_tree_destroy();
+}
+
+EXTENSION_ENTRY("acl_wl", aw_entry);
diff --git a/extensions/acl_wl/acl_wl.h b/extensions/acl_wl/acl_wl.h
new file mode 100644
index 0000000..8606c2a
--- /dev/null
+++ b/extensions/acl_wl/acl_wl.h
@@ -0,0 +1,60 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* Copyright (c) 2011, WIDE Project and NICT *
+* All rights reserved. *
+* *
+* Redistribution and use of this software in source and binary forms, with or without modification, are *
+* permitted provided that the following conditions are met: *
+* *
+* * Redistributions of source code must retain the above *
+* copyright notice, this list of conditions and the *
+* following disclaimer. *
+* *
+* * Redistributions in binary form must reproduce the above *
+* copyright notice, this list of conditions and the *
+* following disclaimer in the documentation and/or other *
+* materials provided with the distribution. *
+* *
+* * Neither the name of the WIDE Project or NICT nor the *
+* names of its contributors may be used to endorse or *
+* promote products derived from this software without *
+* specific prior written permission of WIDE Project and *
+* NICT. *
+* *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
+*********************************************************************************************************/
+
+/* Header file for the acl_wl extension.
+ *
+ * This extension provides a simple mechanism to allow connections from remote peers
+ * without actively maintaining a connection to these peers.
+ *
+ * See the acl_wl.conf.sample file for the format of the configuration file.
+ */
+
+#include <freeDiameter/extension.h>
+
+/* Parse the configuration file */
+int aw_conf_handle(char * conffile);
+
+/* Add to the tree (name is \0 terminated) */
+int aw_tree_add(char * name, int flags);
+
+/* Search in the tree. On return, *result = -1: not found; >=0: found with PI_SEC_* flags */
+int aw_tree_lookup(char * name, int * result);
+
+/* Cleanup the tree */
+void aw_tree_destroy(void);
+
+/* For debug */
+void aw_tree_dump(void);
+
diff --git a/extensions/acl_wl/aw_conf.l b/extensions/acl_wl/aw_conf.l
new file mode 100644
index 0000000..d6b55c8
--- /dev/null
+++ b/extensions/acl_wl/aw_conf.l
@@ -0,0 +1,103 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* Copyright (c) 2013, WIDE Project and NICT *
+* All rights reserved. *
+* *
+* Redistribution and use of this software in source and binary forms, with or without modification, are *
+* permitted provided that the following conditions are met: *
+* *
+* * Redistributions of source code must retain the above *
+* copyright notice, this list of conditions and the *
+* following disclaimer. *
+* *
+* * Redistributions in binary form must reproduce the above *
+* copyright notice, this list of conditions and the *
+* following disclaimer in the documentation and/or other *
+* materials provided with the distribution. *
+* *
+* * Neither the name of the WIDE Project or NICT nor the *
+* names of its contributors may be used to endorse or *
+* promote products derived from this software without *
+* specific prior written permission of WIDE Project and *
+* NICT. *
+* *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
+*********************************************************************************************************/
+
+/* Lex extension's configuration parser.
+ *
+ * The configuration file contains a default priority, and a list of peers with optional overwite priority.
+ * -- see the app_test.conf.sample file for more detail.
+ */
+
+%{
+#include "acl_wl.h"
+/* Include yacc tokens definitions */
+#include "aw_conf.tab.h"
+
+/* Update the column information */
+#define YY_USER_ACTION { \
+ yylloc->first_column = yylloc->last_column + 1; \
+ yylloc->last_column = yylloc->first_column + yyleng - 1; \
+}
+
+/* Avoid warning with newer flex */
+#define YY_NO_INPUT
+
+static int curflag = 0;
+
+%}
+
+%option bison-bridge bison-locations
+%option noyywrap
+%option nounput
+
+%%
+
+ /* Update the line count, reset flag value */
+\n {
+ yylloc->first_line++;
+ yylloc->last_line++;
+ yylloc->last_column=0;
+ curflag = 0;
+ }
+
+ /* Eat all spaces but not new lines */
+([[:space:]]{-}[\n])+ ;
+ /* Eat all comments */
+#.*$ ;
+
+ /* Match the two allowed flags directly in LEX */
+ALLOW_IPSEC {
+ curflag |= PI_SEC_NONE;
+ }
+
+ALLOW_OLD_TLS {
+ curflag |= PI_SEC_TLS_OLD;
+ }
+
+ /* Any other string is considered a fqdn or partial fqdn with a star. The star can only be the first label. */
+(\*|[[:alnum:]][[:alnum:]-]*)(\.[[:alnum:]][[:alnum:]-]*)+ {
+ /* We matched a valid label, let's directly save it into the tree. The function will issue the appropriate warnings. */
+ CHECK_FCT_DO( aw_tree_add(yytext, curflag), return LEX_ERROR);
+ yylval->string = yytext;
+ return FQDN;
+ }
+
+ /* No match */
+<*>[[:alnum:]]+ | /* This rule is only useful to print a complete token in error messages */
+<*>. {
+ TRACE_ERROR("Unrecognized text on line %d col %d: '%s'.", yylloc->first_line, yylloc->first_column, yytext);
+ return LEX_ERROR;
+ }
+
+%%
diff --git a/extensions/acl_wl/aw_conf.y b/extensions/acl_wl/aw_conf.y
new file mode 100644
index 0000000..4ace8e5
--- /dev/null
+++ b/extensions/acl_wl/aw_conf.y
@@ -0,0 +1,141 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* Copyright (c) 2013, WIDE Project and NICT *
+* All rights reserved. *
+* *
+* Redistribution and use of this software in source and binary forms, with or without modification, are *
+* permitted provided that the following conditions are met: *
+* *
+* * Redistributions of source code must retain the above *
+* copyright notice, this list of conditions and the *
+* following disclaimer. *
+* *
+* * Redistributions in binary form must reproduce the above *
+* copyright notice, this list of conditions and the *
+* following disclaimer in the documentation and/or other *
+* materials provided with the distribution. *
+* *
+* * Neither the name of the WIDE Project or NICT nor the *
+* names of its contributors may be used to endorse or *
+* promote products derived from this software without *
+* specific prior written permission of WIDE Project and *
+* NICT. *
+* *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
+*********************************************************************************************************/
+
+/* Yacc extension's configuration parser.
+ * See doc/acl_wl.conf.sample for configuration file format
+ */
+
+/* For development only : */
+%debug
+%error-verbose
+
+/* The parser receives the configuration file filename as parameter */
+%parse-param {char * conffile}
+
+/* Keep track of location */
+%locations
+%pure-parser
+
+%{
+#include "acl_wl.h"
+#include "aw_conf.tab.h" /* bison is not smart enough to define the YYLTYPE before including this code, so... */
+
+#include <string.h>
+#include <errno.h>
+
+/* Forward declaration */
+int yyparse(char * conffile);
+
+static int fqdn_added = 0;
+
+/* Parse the configuration file */
+int aw_conf_handle(char * conffile)
+{
+ extern FILE * aw_confin;
+ int ret;
+
+ TRACE_ENTRY("%p", conffile);
+
+ TRACE_DEBUG (FULL, "Parsing configuration file: %s...", conffile);
+
+ aw_confin = fopen(conffile, "r");
+ if (aw_confin == NULL) {
+ ret = errno;
+ fd_log_debug("Unable to open extension configuration file %s for reading: %s", conffile, strerror(ret));
+ TRACE_DEBUG (INFO, "Error occurred, message logged -- configuration file.");
+ return ret;
+ }
+
+ ret = yyparse(conffile);
+
+ fclose(aw_confin);
+
+ if (ret != 0) {
+ TRACE_DEBUG (INFO, "Unable to parse the configuration file.");
+ return EINVAL;
+ } else {
+ TRACE_DEBUG(FULL, "Read %d FQDN entries successfully.", fqdn_added);
+ }
+
+ return 0;
+}
+
+/* The Lex parser prototype */
+int aw_conflex(YYSTYPE *lvalp, YYLTYPE *llocp);
+
+/* Function to report the errors */
+void yyerror (YYLTYPE *ploc, char * conffile, char const *s)
+{
+ TRACE_DEBUG(INFO, "Error in configuration parsing");
+
+ if (ploc->first_line != ploc->last_line)
+ fd_log_debug("%s:%d.%d-%d.%d : %s", conffile, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s);
+ else if (ploc->first_column != ploc->last_column)
+ fd_log_debug("%s:%d.%d-%d : %s", conffile, ploc->first_line, ploc->first_column, ploc->last_column, s);
+ else
+ fd_log_debug("%s:%d.%d : %s", conffile, ploc->first_line, ploc->first_column, s);
+}
+
+%}
+
+/* Values returned by lex for token */
+%union {
+ char *string;
+}
+
+/* In case of error in the lexical analysis */
+%token LEX_ERROR
+
+/* Key words */
+%token <string> FQDN
+
+
+/* -------------------------------------- */
+%%
+
+ /* The grammar definition */
+conffile: /* empty grammar is OK */
+ | conffile FQDN
+ {
+ fqdn_added++;
+ TRACE_DEBUG(FULL, "Added FQDN: %s", $2);
+ }
+ | conffile LEX_ERROR
+ {
+ yyerror(&yylloc, conffile, "An error occurred while parsing the configuration file");
+ return EINVAL;
+ }
+ ;
+
diff --git a/extensions/acl_wl/aw_tree.c b/extensions/acl_wl/aw_tree.c
new file mode 100644
index 0000000..9b9cbd8
--- /dev/null
+++ b/extensions/acl_wl/aw_tree.c
@@ -0,0 +1,413 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* Copyright (c) 2013, WIDE Project and NICT *
+* All rights reserved. *
+* *
+* Redistribution and use of this software in source and binary forms, with or without modification, are *
+* permitted provided that the following conditions are met: *
+* *
+* * Redistributions of source code must retain the above *
+* copyright notice, this list of conditions and the *
+* following disclaimer. *
+* *
+* * Redistributions in binary form must reproduce the above *
+* copyright notice, this list of conditions and the *
+* following disclaimer in the documentation and/or other *
+* materials provided with the distribution. *
+* *
+* * Neither the name of the WIDE Project or NICT nor the *
+* names of its contributors may be used to endorse or *
+* promote products derived from this software without *
+* specific prior written permission of WIDE Project and *
+* NICT. *
+* *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
+*********************************************************************************************************/
+
+#include "acl_wl.h"
+
+/* The configuration simply contains the allowed fqdn and/or domains (*.example.net)
+ * It is represented similarly to the DNS tree:
+ * (root)--___
+ * / \ \
+ * tld1 tld2 (tld3...)
+ * / |
+ * label1 label2
+ * / \
+ * lbl21 lbl22
+ * / \
+ * lbl211 *
+ *
+ * This tree would whitelist:
+ * - label1.tld1
+ * - lbl211.lbl21.label2.tld2
+ * - *.lbl22.label2.tld2
+ *
+ * The functions to add and search the tree are in aw_tree.c.
+ *
+ */
+
+/* Maximum depth of the tree. We set a static size to avoid dynamic allocations. We report an error if this is not sufficient. */
+#define AW_TREE_MAXDEPTH 10
+
+/* An element of the tree */
+struct tree_item {
+ struct fd_list chain; /* Link to elements at the same level. Ordered alphabetically. */
+ struct fd_list children; /* Sentinel for the subtree. */
+ char * str; /* the \0 terminated label, or NULL if it is a generic container ("*") */
+ int flags; /* PI_SEC_* flags */
+ int leaf; /* true if this item can be a leaf of the tree */
+};
+
+/* The root of the tree */
+static struct fd_list tree_root = FD_LIST_INITIALIZER(tree_root);
+
+/* Note: we don't need to lock, since we add only when parsing the conf, and then read only */
+
+
+/* The parsed name */
+struct split_name {
+ struct {
+ char * str; /* start of this label */
+ size_t len; /* length of this label. It does not include the final "." or "\0" */
+ } label[AW_TREE_MAXDEPTH];
+ int last_lbl; /* idx of last label defined */
+};
+
+/* The following function explodes a name into a split_name structure */
+static int parse_name(char * name, struct split_name * result)
+{
+ int i, l, prev_offset;
+
+ TRACE_ENTRY("%p %p", name, result);
+
+ /* First, initialize the result array */
+ memset(result, 0, sizeof(struct split_name));
+ result->label[0].str = name;
+ l = 0; prev_offset = 0;
+
+ for (i=0; name[i] != '\0'; i++) {
+ if (name[i]=='.') {
+ l++;
+ CHECK_PARAMS( l < AW_TREE_MAXDEPTH );
+
+ /* The previous label is complete, write its size */
+ result->label[l - 1].len = i - prev_offset;
+ prev_offset = i + 1;
+
+ /* Write the start of the new label */
+ result->label[l].str = name + i + 1;
+ }
+ }
+
+ /* Finally, write the size of the last label */
+ result->label[l].len = i - prev_offset;
+
+ result->last_lbl = l;
+
+#if 0
+ fd_log_debug("Parsed name %s as:", name);
+ for (i=0; i<=l; i++)
+ fd_log_debug(" str[%d] len: %d, v:%.*s", i, result->label[i].len, result->label[i].len, result->label[i].str);
+#endif /* 0 */
+ return 0;
+}
+
+/* Create a new tree_item structure */
+static struct tree_item * new_ti(char * str, size_t len, int flags, int leaf)
+{
+ struct tree_item * ti;
+ char * s = NULL;
+
+ TRACE_ENTRY("%p %zd %x", str, len, flags);
+
+ if (str) {
+ CHECK_MALLOC_DO(s = malloc(len + 1), return NULL);
+ memcpy(s, str, len);
+ s[len] = '\0';
+ }
+
+ CHECK_MALLOC_DO( ti = malloc(sizeof(struct tree_item)), {free(s); return NULL; } );
+ memset(ti, 0, sizeof(struct tree_item));
+
+ fd_list_init(&ti->chain, ti);
+ fd_list_init(&ti->children, ti);
+ ti->str = s;
+ ti->flags = flags;
+ ti->leaf = leaf;
+
+ return ti;
+}
+
+/* Recursively delete a subtree */
+static void delete_tree(struct fd_list * senti)
+{
+ while (!FD_IS_LIST_EMPTY(senti)) {
+ struct tree_item * ti = (struct tree_item *)(senti->next);
+
+ /* Delete recursively its children first */
+ delete_tree(&ti->children);
+
+ /* Now, unlink from the sentinel list */
+ fd_list_unlink(&ti->chain);
+
+ /* destroy this tree item */
+ free(ti->str);
+ free(ti);
+ }
+}
+
+/* Top-level destroy function */
+void aw_tree_destroy(void)
+{
+ delete_tree(&tree_root);
+}
+
+/* Display the content of a subtree */
+static void tree_dump(struct fd_list * sub, int indent)
+{
+ struct fd_list * li;
+ for (li = sub->next; li != sub; li = li->next) {
+ struct tree_item * ti = (struct tree_item *)li;
+ char buf[1024];
+ snprintf(buf, sizeof(buf), "%*s%s", indent * 2, "", ti->str?:"*");
+ if (ti->leaf)
+ snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), " (flag:%x)", ti->flags);
+ fd_log_debug("%s", buf);
+ tree_dump(&ti->children, indent + 1);
+ }
+}
+
+/* Top-level function */
+void aw_tree_dump(void)
+{
+ fd_log_debug("[acl_wl] tree dump:");
+ fd_log_debug("(root)");
+ tree_dump(&tree_root, 1);
+ fd_log_debug("[acl_wl] end of dump");
+}
+
+/* Function to add a new entry in the tree */
+int aw_tree_add(char * name, int flags)
+{
+ struct split_name sn;
+ struct tree_item * ti;
+ struct fd_list * li, *senti;
+ int lbl, found;
+
+ TRACE_ENTRY("%p %x", name, flags);
+ CHECK_PARAMS(name && *name);
+
+ CHECK_FCT_DO( parse_name(name, &sn),
+ {
+ fd_log_debug("The name '%s' contains too many labels, try a generic (*) or recompile with bigger AW_TREE_MAXDEPTH value (cur: %d)", name, AW_TREE_MAXDEPTH);
+ return EINVAL;
+ } );
+
+ senti = &tree_root;
+
+ for (lbl = sn.last_lbl; lbl > 0; lbl--) {
+ /* Check if the list is empty, we can directly create the new entry */
+ if (FD_IS_LIST_EMPTY(senti)) {
+ CHECK_MALLOC( ti = new_ti(sn.label[lbl].str, sn.label[lbl].len, 0, 0 /* flags are only set in the terminals */) );
+ /* Insert this label in the sentinel sublist */
+ fd_list_insert_after(senti, &ti->chain);
+ /* Update the sentinel */
+ senti = &ti->children;
+ /* loop to the next label */
+ continue;
+ }
+
+ /* Check if we have a '*' element already that overlapses */
+ ti = (struct tree_item *)(senti->next);
+ if (ti->str == NULL) {
+ fd_log_debug("[acl_wl] Warning: entry '%s' is superseeded by a generic entry at label %d, ignoring.", name, lbl + 1);
+ return 0;
+ }
+
+ /* Search this label in the ordered list */
+ found = 0;
+ for (li = senti->next; li != senti; li=li->next) {
+ int cmp, len;
+ ti = (struct tree_item *)li;
+
+ cmp = strncasecmp(ti->str, sn.label[lbl].str, sn.label[lbl].len);
+ if (cmp > 0)
+ break; /* the new label must be inserted before li */
+ if (cmp < 0)
+ continue;
+
+ /* Check the lengths */
+ len = strlen(ti->str);
+ if (len > sn.label[lbl].len)
+ break; /* the new label must be inserted before li */
+ if (len < sn.label[lbl].len)
+ continue;
+
+ /* We already had this label */
+ found = 1;
+ senti = &ti->children;
+ break;
+ }
+
+ if (found)
+ continue;
+
+ /* Otherwise, we have to create a new ti, and add it before li */
+ CHECK_MALLOC( ti = new_ti(sn.label[lbl].str, sn.label[lbl].len, 0, 0 /* flags are only set in the terminals */) );
+ /* Insert this label in the sentinel sublist */
+ fd_list_insert_before(li, &ti->chain);
+ /* Update the sentinel */
+ senti = &ti->children;
+ }
+
+ ti = NULL;
+ li = senti;
+
+ /* At this point, senti points to the list where we are supposed to insert our last label. */
+ if (sn.label[0].str[0] == '*') {
+ if (!FD_IS_LIST_EMPTY(senti)) {
+ fd_log_debug("[acl_wl] Warning: entry '%s' overwrites previous more detailed entries, these are deleted.", name);
+ delete_tree(senti);
+ }
+
+ /* Create the new entry */
+ CHECK_MALLOC( ti = new_ti(NULL, 0, flags, 1) );
+ } else {
+ if (!FD_IS_LIST_EMPTY(senti)) {
+ /* Check we don't have a '*' entry already */
+ ti = (struct tree_item *)(senti->next);
+ if (ti->str == NULL) {
+ fd_log_debug("[acl_wl] Warning: entry '%s' is superseeded by a generic entry at label 1, ignoring.", name);
+ return 0;
+ }
+
+ /* Search the place for the new label */
+ for (li = senti->next; li != senti; li=li->next) {
+ int cmp, len;
+ ti = (struct tree_item *)li;
+
+ cmp = strncasecmp(ti->str, sn.label[0].str, sn.label[0].len);
+ if (cmp > 0)
+ break; /* the new label must be inserted before li */
+ if (cmp < 0)
+ continue;
+
+ /* Check the lengths */
+ len = strlen(ti->str);
+ if (len > sn.label[0].len)
+ break; /* the new label must be inserted before li */
+ if (len < sn.label[0].len)
+ continue;
+
+ /* We already had this label */
+ if (ti->leaf) {
+ fd_log_debug("[acl_wl] Warning: entry '%s' is duplicated, merging the flags.", name);
+ ti->flags |= flags;
+ return 0;
+ } else {
+ /* Just mark this entry as a valid leaf also */
+ ti->leaf = 1;
+ ti->flags = flags;
+ return 0;
+ }
+ }
+ }
+
+ /* Create the new entry */
+ CHECK_MALLOC( ti = new_ti(sn.label[0].str, sn.label[0].len, flags, 1) );
+ }
+
+ /* The new label is "ti", it is inserted before "li" */
+ fd_list_insert_before(li, &ti->chain);
+
+ /* Done! */
+ return 0;
+}
+
+/* Search in the tree. On return, *result = -1: not found; >=0: found with PI_SEC_* flags */
+int aw_tree_lookup(char * name, int * result)
+{
+ struct split_name sn;
+ int lbl, found;
+ struct tree_item * ti;
+ struct fd_list * senti, *li;
+
+ TRACE_ENTRY("%p %p", name, result);
+ CHECK_PARAMS(name && result);
+
+ /* Initialize */
+ *result = -1;
+
+ /* Parse the name into labels */
+ CHECK_FCT_DO( parse_name(name, &sn),
+ {
+ TRACE_DEBUG(INFO, "Too many labels in this name, it cannot be found in the tree, skipping.");
+ return 0;
+ } );
+
+ senti = &tree_root;
+
+ for (lbl = sn.last_lbl; lbl >= 0; lbl--) {
+ /* Check if the list is empty, we can directly return */
+ if (FD_IS_LIST_EMPTY(senti)) {
+ /* The item is not found */
+ return 0;
+ }
+
+ /* Check if we have a '*' element */
+ ti = (struct tree_item *)(senti->next);
+ if (ti->str == NULL) {
+ TRACE_DEBUG(ANNOYING, "[acl_wl] %s matched at label %d with a generic entry.", name, lbl + 1);
+ *result = ti->flags;
+ return 0;
+ }
+
+ /* Search this label in the ordered list */
+ found = 0;
+ for (li = senti->next; li != senti; li=li->next) {
+ int cmp, len;
+ ti = (struct tree_item *)li;
+
+ cmp = strncasecmp(ti->str, sn.label[lbl].str, sn.label[lbl].len);
+ if (cmp > 0)
+ return 0; /* the label was not found */
+ if (cmp < 0)
+ continue;
+
+ /* Check the lengths */
+ len = strlen(ti->str);
+ if (len > sn.label[lbl].len)
+ return 0; /* the label was not found */
+ if (len < sn.label[lbl].len)
+ continue;
+
+ /* We found the label */
+ found = 1;
+ senti = &ti->children;
+ break;
+ }
+
+ if (!found)
+ return 0; /* label not found */
+
+ /* otherwise, continue, sentinel has been updated */
+ }
+
+ /* At the end, ti points to the correct leaf */
+ if (!ti->leaf)
+ return 0;
+
+ TRACE_DEBUG(ANNOYING, "[acl_wl] %s matched exactly.", name);
+ *result = ti->flags;
+ return 0;
+}