summaryrefslogtreecommitdiffstats
path: root/src/scanner.l
diff options
context:
space:
mode:
authorIsmo Puustinen <ismo.puustinen@intel.com>2017-06-27 15:14:58 +0300
committerPablo Neira Ayuso <pablo@netfilter.org>2017-06-27 18:08:51 +0200
commit8368e63eff044b78b9332f3af398a940d75e590f (patch)
tree4e690b118dbcdfea85c6c628f21e2ea8124cd8eb /src/scanner.l
parent1325419d43184ffe426878af2a2f050e28702852 (diff)
scanner: support for wildcards in include statements.
Use glob() to find paths in include statements. The rules are these: 1. If no files can be found in the pattern with wildcards, do not return an error. 2. Do not match any files beginning with '.'. 3. Do not handle include directories anymore. For example, the statement: include "foo/" would now need to be rewritten: include "foo/*" Signed-off-by: Ismo Puustinen <ismo.puustinen@intel.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'src/scanner.l')
-rw-r--r--src/scanner.l226
1 files changed, 107 insertions, 119 deletions
diff --git a/src/scanner.l b/src/scanner.l
index f220e593..86a03f3b 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -10,15 +10,12 @@
%{
-#include <dirent.h>
-#include <libgen.h>
#include <limits.h>
-#include <unistd.h>
+#include <glob.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/types.h>
#include <linux/netfilter.h>
-#include <sys/stat.h>
#include <nftables.h>
#include <erec.h>
@@ -664,128 +661,108 @@ err:
return -1;
}
-int scanner_read_file(void *scanner, const char *filename,
- const struct location *loc)
-{
- return include_file(scanner, filename, loc);
-}
-
-static int directoryfilter(const struct dirent *de)
+static int include_glob(void *scanner, const char *pattern,
+ const struct location *loc)
{
- if (strcmp(de->d_name, ".") == 0 ||
- strcmp(de->d_name, "..") == 0)
- return 0;
+ struct parser_state *state = yyget_extra(scanner);
+ struct error_record *erec = NULL;
+ bool wildcard = false;
+ glob_t glob_data;
+ unsigned int i;
+ int flags = 0;
+ int ret;
+ char *p;
+
+ /* This function can return four meaningful values:
+ *
+ * -1 means that there was an error.
+ * 0 means that a single non-wildcard match was done.
+ * 1 means that there are no wildcards in the pattern and the
+ * search can continue.
+ * 2 means that there are wildcards in the pattern and the search
+ * can continue.
+ *
+ * The diffrence is needed, because there is a semantic difference
+ * between patterns with wildcards and no wildcards. Not finding a
+ * non-wildcard file is an error but not finding any matches for a
+ * wildcard pattern is not.
+ */
- /* Accept other filenames. If we want to enable filtering based on
- * filename suffix (*.nft), this would be the place to do it.
+ /* There shouldn't be a need to use escape characters in include paths.
*/
- return 1;
-}
+ flags |= GLOB_NOESCAPE;
-static void free_scandir_des(struct dirent **des, int n_des)
-{
- int i;
+ /* Mark directories so we can filter them out (also links). */
+ flags |= GLOB_MARK;
- for (i = 0; i < n_des; i++)
- free(des[i]);
+ /* If there is no match, glob() doesn't set GLOB_MAGCHAR even if there
+ * are wildcard characters in the pattern. We need to look for (luckily
+ * well-known and not likely to change) magic characters ourselves. In a
+ * perfect world, we could use glob() itself to figure out if there are
+ * wildcards in the pattern.
+ */
+ p = (char *)pattern;
+ while (*p) {
+ if (*p == '*' || *p == '?' || *p == '[') {
+ wildcard = true;
+ break;
+ }
+ p++;
+ }
- free(des);
-}
+ ret = glob(pattern, flags, NULL, &glob_data);
+ if (ret == 0) {
+ char *path;
+ int len;
-static int include_directory(void *scanner, const char *dirname,
- const struct location *loc)
-{
- struct parser_state *state = yyget_extra(scanner);
- struct dirent **des = NULL;
- struct error_record *erec;
- int ret, n_des = 0, i;
- char dirbuf[PATH_MAX];
- FILE *f;
+ /* reverse alphabetical order due to stack */
+ for (i = glob_data.gl_pathc; i > 0; i--) {
- if (!dirname[0] || dirname[strlen(dirname)-1] != '/') {
- erec = error(loc, "Include directory name \"%s\" does not end in '/'",
- dirname);
- goto err;
- }
+ path = glob_data.gl_pathv[i-1];
- /* If the path is a directory, assume that all files there need
- * to be included. Sort the file list in alphabetical order.
- */
- n_des = scandir(dirname, &des, directoryfilter, alphasort);
- if (n_des < 0) {
- erec = error(loc, "Failed to scan directory contents for \"%s\"",
- dirname);
- goto err;
- } else if (n_des == 0) {
- /* nothing to do */
- free(des);
- return 0;
- }
+ /* ignore directories */
+ len = strlen(path);
+ if (len == 0 || path[len - 1] == '/')
+ continue;
- /* We need to push the files in reverse order, so that they will be
- * popped in the correct order.
- */
- for (i = n_des - 1; i >= 0; i--) {
- ret = snprintf(dirbuf, sizeof(dirbuf), "%s/%s", dirname,
- des[i]->d_name);
- if (ret < 0 || ret >= PATH_MAX) {
- erec = error(loc, "Too long file path \"%s/%s\"\n",
- dirname, des[i]->d_name);
- goto err;
+ ret = include_file(scanner, path, loc);
+ if (ret != 0)
+ goto err;
}
- f = fopen(dirbuf, "r");
- if (f == NULL) {
- erec = error(loc, "Could not open file \"%s\": %s\n",
- dirbuf, strerror(errno));
- goto err;
- }
+ globfree(&glob_data);
+
+ /* If no wildcards and we found the file, stop the search (with
+ * 0). In case of wildcards we need to still continue the
+ * search, because other matches might be in other include
+ * directories. We handled the case with a non-wildcard pattern
+ * and no matches already before.
+ */
+ return wildcard ? 2 : 0;
+ } else if (ret == GLOB_NOMATCH) {
+ globfree(&glob_data);
- erec = scanner_push_file(scanner, des[i]->d_name, f, loc);
- if (erec != NULL)
- goto err;
+ /* We need to tell the caller whether wildcards were used in
+ * case of no match, because the semantics for handling the
+ * cases are different.
+ */
+ return wildcard ? 2 : 1;
}
- free_scandir_des(des, n_des);
- return 0;
+
+ erec = error(loc, "Failed to glob the pattern %s", pattern);
+
+ /* intentional fall through */
err:
- free_scandir_des(des, n_des);
- erec_queue(erec, state->msgs);
+ if (erec)
+ erec_queue(erec, state->msgs);
+ globfree(&glob_data);
return -1;
}
-static int include_dentry(void *scanner, const char *filename,
- const struct location *loc)
+int scanner_read_file(void *scanner, const char *filename,
+ const struct location *loc)
{
- struct parser_state *state = yyget_extra(scanner);
- struct error_record *erec;
- struct stat st;
- int ret;
-
- ret = stat(filename, &st);
- if (ret == -1 && errno == ENOENT) {
- /* Could not find the directory or file, keep on searching.
- * Return value '1' indicates to the caller that we should still
- * search in the next include directory.
- */
- return 1;
- } else if (ret == 0) {
- if (S_ISDIR(st.st_mode))
- return include_directory(scanner, filename, loc);
- else if (S_ISREG(st.st_mode))
- return include_file(scanner, filename, loc);
- else {
- errno = EINVAL;
- ret = -1;
- }
- }
-
- /* Process error for failed stat and cases where the file is not a
- * directory or (a link to) a regular file.
- */
- erec = error(loc, "Failed to access file \"%s\": %s\n",
- filename, strerror(errno));
- erec_queue(erec, state->msgs);
- return ret;
+ return include_file(scanner, filename, loc);
}
static bool search_in_include_path(const char *filename)
@@ -802,7 +779,7 @@ int scanner_include_file(void *scanner, const char *filename,
struct error_record *erec;
char buf[PATH_MAX];
unsigned int i;
- int ret;
+ int ret = -1;
if (search_in_include_path(filename)) {
for (i = 0; i < INCLUDE_PATHS_MAX; i++) {
@@ -817,23 +794,34 @@ int scanner_include_file(void *scanner, const char *filename,
return -1;
}
- ret = include_dentry(scanner, buf, loc);
+ ret = include_glob(scanner, buf, loc);
+
+ /* error was already handled */
+ if (ret == -1)
+ return -1;
+ /* no wildcards and file was processed: break early. */
if (ret == 0)
return 0;
- else if (ret != 1)
- /* error has been processed already */
- return -1;
+
+ /* else 1 (no wildcards) or 2 (wildcards): keep
+ * searching.
+ */
}
} else {
- ret = include_dentry(scanner, filename, loc);
- if (ret == 0)
- return 0;
- else if (ret != 1)
- return -1;
- /* else fall through to "not found" processing */
+ /* an absolute path (starts with '/') */
+ ret = include_glob(scanner, filename, loc);
}
- erec = error(loc, "Did not find \"%s\"\n", filename);
+ /* handle the case where no file was found */
+ if (ret == -1)
+ return -1;
+ else if (ret == 0 || ret == 2)
+ return 0;
+
+ /* 1 means an error, because there are no more include directories to
+ * search, and the pattern does not have wildcard characters.
+ */
+ erec = error(loc, "File not found: %s", filename);
erec_queue(erec, state->msgs);
return -1;
}