autofs-5.0.9 - amd lookup add selector handling
From: Ian Kent <raven@themaw.net>
---
include/parse_subs.h | 2
lib/parse_subs.c | 127 ++++++++++++++++++
modules/amd_parse.y | 49 ++++++-
modules/amd_tok.l | 73 ++++++++++
modules/parse_amd.c | 348 ++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 586 insertions(+), 13 deletions(-)
diff --git a/include/parse_subs.h b/include/parse_subs.h
index a416c59..43da182 100644
--- a/include/parse_subs.h
+++ b/include/parse_subs.h
@@ -111,6 +111,8 @@ struct map_type_info {
};
unsigned int get_proximity(struct sockaddr *);
+unsigned int get_network_proximity(const char *);
+unsigned int in_network(char *);
const char *skipspace(const char *);
int check_colon(const char *);
int chunklen(const char *, int);
diff --git a/lib/parse_subs.c b/lib/parse_subs.c
index f485a4c..de5319f 100644
--- a/lib/parse_subs.c
+++ b/lib/parse_subs.c
@@ -19,9 +19,13 @@
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
+#include <sys/socket.h>
#include <ifaddrs.h>
#include <libgen.h>
#include <net/if.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
#include "automount.h"
#define MAX_OPTIONS_LEN 256
@@ -370,6 +374,129 @@ unsigned int get_proximity(struct sockaddr *host_addr)
return PROXIMITY_OTHER;
}
+static char *inet_fill_net(const char *net_num, char *net)
+{
+ char *np;
+ unsigned int dots = 3;
+
+ if (strlen(net_num) > INET_ADDRSTRLEN)
+ return NULL;
+
+ if (!isdigit(*net_num))
+ return NULL;
+
+ *net = '\0';
+ strcpy(net, net_num);
+
+ np = net;
+ while (*np++) {
+ if (*np == '.') {
+ np++;
+ dots--;
+ if (!*np && dots)
+ strcat(net, "0");
+ continue;
+ }
+
+ if ((*np && !isdigit(*np)) || dots < 0) {
+ *net = '\0';
+ return NULL;
+ }
+ }
+
+ while (dots--)
+ strcat(net, ".0");
+
+ return net;
+}
+
+static char *get_network_number(const char *network)
+{
+ struct netent *netent;
+ char cnet[MAX_NETWORK_LEN];
+ uint32_t h_net;
+ size_t len;
+
+ len = strlen(network) + 1;
+ if (len > MAX_NETWORK_LEN)
+ return NULL;
+
+ netent = getnetbyname(network);
+ if (!netent)
+ return NULL;
+ h_net = ntohl(netent->n_net);
+
+ if (!inet_ntop(AF_INET, &h_net, cnet, INET_ADDRSTRLEN))
+ return NULL;
+
+ return strdup(cnet);
+}
+
+unsigned int get_network_proximity(const char *name)
+{
+ struct addrinfo hints;
+ struct addrinfo *ni, *this;
+ char name_or_num[NI_MAXHOST];
+ unsigned int proximity;
+ char *net;
+ int ret;
+
+ if (!name)
+ return PROXIMITY_ERROR;
+
+ net = get_network_number(name);
+ if (net)
+ strcpy(name_or_num, net);
+ else {
+ char this[NI_MAXHOST];
+ char *mask;
+
+ strcpy(this, name);
+ if ((mask = strchr(this, '/')))
+ *mask++ = '\0';
+ if (!strchr(this, '.'))
+ strcpy(name_or_num, this);
+ else {
+ char buf[NI_MAXHOST], *new;
+ new = inet_fill_net(this, buf);
+ if (!new)
+ return PROXIMITY_ERROR;
+ strcpy(name_or_num, new);
+ }
+ }
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ ret = getaddrinfo(name_or_num, NULL, &hints, &ni);
+ if (ret) {
+ logerr("getaddrinfo: %s", gai_strerror(ret));
+ return PROXIMITY_ERROR;
+ }
+
+ proximity = PROXIMITY_OTHER;
+
+ this = ni;
+ while (this) {
+ unsigned int prx = get_proximity(this->ai_addr);
+ if (prx < proximity)
+ proximity = prx;
+ this = this->ai_next;
+ }
+
+ return proximity;
+}
+
+unsigned int in_network(char *network)
+{
+ unsigned int proximity = get_network_proximity(network);
+ if (proximity == PROXIMITY_ERROR ||
+ proximity > PROXIMITY_SUBNET)
+ return 0;
+ return 1;
+}
+
/*
* Skip whitespace in a string; if we hit a #, consider the rest of the
* entry a comment.
diff --git a/modules/amd_parse.y b/modules/amd_parse.y
index 33106a1..71fd569 100644
--- a/modules/amd_parse.y
+++ b/modules/amd_parse.y
@@ -92,6 +92,9 @@ static int amd_fprintf(FILE *, char *, ...);
%token NOT_EQUAL
%token COMMA
%token OPTION_ASSIGN
+%token LBRACKET
+%token RBRACKET
+%token NOT
%token NILL
%token <strtype> MAP_OPTION
@@ -102,6 +105,7 @@ static int amd_fprintf(FILE *, char *, ...);
%token <strtype> MNT_OPTION
%token <strtype> SELECTOR
%token <strtype> SELECTOR_VALUE
+%token <strtype> SEL_ARG_VALUE
%token <strtype> OPTION
%token <strtype> MACRO
%token <strtype> OTHER
@@ -187,18 +191,46 @@ selector_or_option: selection
selection: SELECTOR IS_EQUAL SELECTOR_VALUE
{
- if (!make_selector($1, $3, NULL, SEL_TYPE_EQUAL)) {
+ if (!make_selector($1, $3, NULL, SEL_COMP_EQUAL)) {
amd_notify($1);
YYABORT;
}
}
| SELECTOR NOT_EQUAL SELECTOR_VALUE
{
- if (!make_selector($1, $3, NULL, SEL_TYPE_NOTEQUAL)) {
+ if (!make_selector($1, $3, NULL, SEL_COMP_NOTEQUAL)) {
amd_notify($1);
YYABORT;
}
}
+ | SELECTOR LBRACKET SEL_ARG_VALUE RBRACKET
+ {
+ if (!make_selector($1, $3, NULL, SEL_COMP_NONE)) {
+ amd_notify($1);
+ YYABORT;
+ }
+ }
+ | SELECTOR LBRACKET SEL_ARG_VALUE COMMA SEL_ARG_VALUE RBRACKET
+ {
+ if (!make_selector($1, $3, $5, SEL_COMP_NONE)) {
+ amd_notify($1);
+ YYABORT;
+ }
+ }
+ | NOT SELECTOR LBRACKET SEL_ARG_VALUE RBRACKET
+ {
+ if (!make_selector($2, $4, NULL, SEL_COMP_NOT)) {
+ amd_notify($2);
+ YYABORT;
+ }
+ }
+ | NOT SELECTOR LBRACKET SEL_ARG_VALUE COMMA SEL_ARG_VALUE RBRACKET
+ {
+ if (!make_selector($2, $4, $6, SEL_COMP_NOT)) {
+ amd_notify($2);
+ YYABORT;
+ }
+ }
;
option_assignment: MAP_OPTION OPTION_ASSIGN FS_TYPE
@@ -388,9 +420,6 @@ static int make_selector(char *name,
if (!sel_lookup(name))
return 0;
- if (!value1)
- return 0;
-
s = get_selector(name);
if (!s)
return 0;
@@ -401,9 +430,13 @@ static int make_selector(char *name,
goto error;
s->comp.value = tmp;
} else if (s->sel->flags & SEL_FLAG_FUNC1) {
- char *tmp = amd_strdup(value1);
- if (!tmp)
- goto error;
+ if (!value1)
+ tmp = NULL;
+ else {
+ char *tmp = amd_strdup(value1);
+ if (!tmp)
+ goto error;
+ }
s->func.arg1 = tmp;
} else if (s->sel->flags & SEL_FLAG_FUNC2) {
char *tmp = amd_strdup(value1);
diff --git a/modules/amd_tok.l b/modules/amd_tok.l
index afa3a87..cea9ea5 100644
--- a/modules/amd_tok.l
+++ b/modules/amd_tok.l
@@ -70,22 +70,29 @@ int amd_yyinput(char *, int);
%option nounput
-%x MAPOPTVAL FSOPTVAL MNTOPTVAL SELOPTVAL
+%x MAPOPTVAL FSOPTVAL MNTOPTVAL SELOPTVAL SELARGVAL
NL \r?\n
OPTWS [[:blank:]]*
OTHR [^!;:=/|\- \t\r\n#]*
+V4NUM ([01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5])
+
MACRO (\$\{([[:alpha:]_/]([[:alnum:]_\-])([[:alnum:]_\-/])*)\})
QSTR (\"([^"\\]|\\.)*\")
OSTR ([[:alpha:]]([[:alnum:]_\-])+)
FSTR ([[:alnum:]_/\.]([[:alnum:]_\-/\.]|(\\.))*)
VSTR (([[:alnum:]_\-\:/\.])+)
SSTR ([[:alpha:]]([[:alnum:]\-\.])+)
+IP4ADDR ({V4NUM}\.((({V4NUM}\.){0,2}){V4NUM}?))
+V4MASK ({IP4ADDR}|([1-2][0-9]|3[0-2]|[1-9]))
+IP6ADDR ((([A-Fa-f0-9]{1,4}\:\:?){1,7}[A-Fa-f0-9]{1,4})|(\:\:1))
+V6MASK (12[0-8]|1[0-1][0-9]|[1-9][0-9]|[1-9])
FOPT (({QSTR}|{FSTR}|{MACRO})+)
OPTS ({OSTR}(=({VSTR}|{QSTR}|{MACRO})+)?)
SOPT (({SSTR}|{QSTR}|{MACRO})+)
+NOPT ({SSTR}|(({IP4ADDR}(\/{V4MASK})?)|({IP6ADDR}(\/{V6MASK})?)))
MAPOPT (fs|type|maptype|pref|sublink|delay)
MNTOPT (opts|addopts|remopts)
@@ -94,13 +101,16 @@ MAPTYPE (file|nis|nisplus|ldap|hesiod|exec|ndbm|passwd|union)
FSTYPE (auto|nfs|link|host|nfsx|ufs|xfs|efs)
OSSEL (arch|karch|os|osver|full_os|vendor)
-HSTSEL (host|hostd|xhost|domain|byte|cluster)
-NETSEL (netnumber|network|wire|netgrp|netgrpd|in_network)
+HSTSEL (host|hostd|domain|byte|cluster)
+NETSEL (netnumber|network|wire|in_network)
USRSEL (uid|gid)
MAPSEL (key|map|path)
-OTRSEL (exists|autodir|dollar)
+OTRSEL (autodir|dollar)
BOLSEL (true|false)
-SELOPT ({OSSEL}|{HSTSEL}|{NETSEL}|{BOLSEL}|{USRSEL}|{MAPSEL}|{OTRSEL})
+
+SELOPT ({OSSEL}|{HSTSEL}|{USRSEL}|{MAPSEL}|{OTRSEL})
+SEL1ARG (xhost|exists|{NETSEL}|{BOLSEL})
+SEL2ARG (netgrp|netgrpd)
CUTSEP (\|\||\/)
@@ -135,6 +145,20 @@ CUTSEP (\|\||\/)
return SELECTOR;
}
+ "!"/({SEL1ARG}|{SEL2ARG}) { return NOT; }
+
+ {SEL1ARG} {
+ BEGIN(SELARGVAL);
+ strcpy(amd_lval.strtype, amd_text);
+ return SELECTOR;
+ }
+
+ {SEL2ARG} {
+ BEGIN(SELARGVAL);
+ strcpy(amd_lval.strtype, amd_text);
+ return SELECTOR;
+ }
+
{CUTSEP} { return CUT; }
"-" { return HYPHEN; }
@@ -263,6 +287,45 @@ CUTSEP (\|\||\/)
}
}
+<SELARGVAL>{
+ {NL} |
+ \x00 {
+ BEGIN(INITIAL);
+ yyless(1);
+ }
+
+ ";" {
+ BEGIN(INITIAL);
+ return SEPERATOR;
+ }
+
+ "(" { return LBRACKET; }
+
+ {NOPT} {
+ strcpy(amd_lval.strtype, amd_text);
+ return SEL_ARG_VALUE;
+ }
+
+ {SOPT}/"," {
+ strcpy(amd_lval.strtype, amd_text);
+ return SEL_ARG_VALUE;
+ }
+
+ "," { return COMMA; }
+
+ {SOPT} {
+ strcpy(amd_lval.strtype, amd_text);
+ return SEL_ARG_VALUE;
+ }
+
+ {FOPT} {
+ strcpy(amd_lval.strtype, amd_text);
+ return SEL_ARG_VALUE;
+ }
+
+ ")" { return RBRACKET; }
+}
+
%%
#include "automount.h"
diff --git a/modules/parse_amd.c b/modules/parse_amd.c
index 35cc5dc..d9c7d9b 100644
--- a/modules/parse_amd.c
+++ b/modules/parse_amd.c
@@ -223,6 +223,307 @@ static struct substvar *add_lookup_vars(struct autofs_point *ap,
return list;
}
+static int match_my_name(unsigned int logopt, const char *name, struct substvar *sv)
+{
+ struct addrinfo hints, *cni, *ni, *haddr;
+ char host[NI_MAXHOST + 1], numeric[NI_MAXHOST + 1];
+ const struct substvar *v;
+ int rv = 0, ret;
+
+ v = macro_findvar(sv, "host", 4);
+ if (v) {
+ if (!strcmp(v->val, name))
+ return 1;
+ }
+
+ /* Check if comparison value is an alias */
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ /* Get host canonical name */
+ ret = getaddrinfo(v->val, NULL, &hints, &cni);
+ if (ret) {
+ error(logopt,
+ "hostname lookup failed: %s\n", gai_strerror(ret));
+ goto out;
+ }
+
+ hints.ai_flags = 0;
+
+ /* Resolve comparison name to its names and compare */
+ ret = getaddrinfo(name, NULL, &hints, &ni);
+ if (ret) {
+ error(logopt,
+ "hostname lookup failed: %s\n", gai_strerror(ret));
+ freeaddrinfo(cni);
+ goto out;
+ }
+
+ haddr = ni;
+ while (haddr) {
+ /* Translate the host address into a numeric string form */
+ ret = getnameinfo(haddr->ai_addr, haddr->ai_addrlen,
+ numeric, sizeof(numeric), NULL, 0,
+ NI_NUMERICHOST);
+ if (ret) {
+ error(logopt,
+ "host address info lookup failed: %s\n",
+ gai_strerror(ret));
+ freeaddrinfo(cni);
+ goto next;
+ }
+
+ /* Try to resolve back again to get the canonical name */
+ ret = getnameinfo(haddr->ai_addr, haddr->ai_addrlen,
+ host, NI_MAXHOST, NULL, 0, 0);
+ if (ret) {
+ error(logopt,
+ "host address info lookup failed: %s\n",
+ gai_strerror(ret));
+ freeaddrinfo(cni);
+ goto next;
+ }
+
+ if (!strcmp(host, cni->ai_canonname)) {
+ rv = 1;
+ break;
+ }
+next:
+ haddr = haddr->ai_next;
+ }
+ freeaddrinfo(ni);
+ freeaddrinfo(cni);
+out:
+ return rv;
+}
+
+static int eval_selector(unsigned int logopt,
+ struct amd_entry *this, struct substvar *sv)
+{
+ struct selector *s = this->selector;
+ const struct substvar *v;
+ unsigned int s_type;
+ unsigned int v_type;
+ struct stat st;
+ char *host;
+ int res, val, ret = 0;
+
+ s_type = s->sel->flags & SEL_FLAGS_TYPE_MASK;
+
+ switch (s_type) {
+ case SEL_FLAG_MACRO:
+ v = macro_findvar(sv, s->sel->name, strlen(s->sel->name));
+ if (!v) {
+ error(logopt, "failed to get selector %s", s->sel->name);
+ return 0;
+ }
+
+ v_type = s->sel->flags & SEL_FLAGS_VALUE_MASK;
+
+ switch (v_type) {
+ case SEL_FLAG_STR:
+ res = strcmp(v->val, s->comp.value);
+ if (s->compare & SEL_COMP_EQUAL && !res) {
+ debug(logopt, MODPREFIX
+ "matched selector %s(%s) == %s",
+ v->def, v->val, s->comp.value);
+ ret = 1;
+ break;
+ } else if (s->compare & SEL_COMP_NOTEQUAL && res) {
+ debug(logopt, MODPREFIX
+ "matched selector %s(%s) != %s",
+ v->def, v->val, s->comp.value);
+ ret = 1;
+ break;
+ }
+
+ debug(logopt, MODPREFIX
+ "did not match selector %s(%s) %s %s",
+ v->def, v->val,
+ (s->compare & SEL_COMP_EQUAL ? "==" : "!="),
+ s->comp.value);
+ break;
+
+ case SEL_FLAG_NUM:
+ res = atoi(v->val);
+ val = atoi(s->comp.value);
+ if (s->compare & SEL_COMP_EQUAL && res == val) {
+ debug(logopt, MODPREFIX
+ "matched selector %s(%s) equal to %s",
+ v->def, v->val, s->comp.value);
+ ret = 1;
+ break;
+ } else if (s->compare & SEL_COMP_NOTEQUAL && res != val) {
+ debug(logopt, MODPREFIX
+ "matched selector %s(%s) not equal to %s",
+ v->def, v->val, s->comp.value);
+ ret = 1;
+ break;
+ }
+
+ debug(logopt, MODPREFIX
+ "did not match selector %s(%s) %s %s",
+ v->def, v->val,
+ (s->compare & SEL_COMP_EQUAL ? "==" : "!="),
+ s->comp.value);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SEL_FLAG_FUNC1:
+ if (s->sel->selector != SEL_TRUE &&
+ s->sel->selector != SEL_FALSE &&
+ !s->func.arg1) {
+ error(logopt, MODPREFIX
+ "expected argument missing for selector %s",
+ s->sel->name);
+ break;
+ }
+
+ switch (s->sel->selector) {
+ case SEL_TRUE:
+ ret = 1;
+ if (s->compare == SEL_COMP_NOT)
+ ret = !ret;
+ if (ret)
+ debug(logopt, MODPREFIX
+ "matched selector %s(%s)",
+ s->sel->name, s->func.arg1);
+ else
+ debug(logopt, MODPREFIX
+ "did not match selector %s(%s)",
+ s->sel->name, s->func.arg1);
+ break;
+
+ case SEL_FALSE:
+ if (s->compare == SEL_COMP_NOT)
+ ret = !ret;
+ if (ret)
+ debug(logopt, MODPREFIX
+ "matched selector %s(%s)",
+ s->sel->name, s->func.arg1);
+ else
+ debug(logopt, MODPREFIX
+ "did not match selector %s(%s)",
+ s->sel->name, s->func.arg1);
+ break;
+
+ case SEL_XHOST:
+ ret = match_my_name(logopt, s->func.arg1, sv);
+ if (s->compare == SEL_COMP_NOT)
+ ret = !ret;
+ if (ret)
+ debug(logopt, MODPREFIX
+ "matched selector %s(%s) to host name",
+ s->sel->name, s->func.arg1);
+ else
+ debug(logopt, MODPREFIX
+ "did not match selector %s(%s) to host name",
+ s->sel->name, s->func.arg1);
+ break;
+
+ case SEL_EXISTS:
+ /* Sould be OK to fail on any error here */
+ ret = !lstat(s->func.arg1, &st);
+ if (s->compare == SEL_COMP_NOT)
+ ret = !ret;
+ if (ret)
+ debug(logopt, MODPREFIX
+ "matched selector %s(%s)",
+ s->sel->name, s->func.arg1);
+ else
+ debug(logopt, MODPREFIX
+ "did not match selector %s(%s)",
+ s->sel->name, s->func.arg1);
+ break;
+
+ case SEL_IN_NETWORK:
+ ret = in_network(s->func.arg1);
+ if (s->compare == SEL_COMP_NOT)
+ ret = !ret;
+ if (ret)
+ debug(logopt, MODPREFIX
+ "matched selector %s(%s)",
+ s->sel->name, s->func.arg1);
+ else
+ debug(logopt, MODPREFIX
+ "did not match selector %s(%s)",
+ s->sel->name, s->func.arg1);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SEL_FLAG_FUNC2:
+ if (!s->func.arg1) {
+ error(logopt, MODPREFIX
+ "expected argument missing for selector %s",
+ s->sel->name);
+ break;
+ }
+
+ switch (s->sel->selector) {
+ case SEL_NETGRP:
+ case SEL_NETGRPD:
+ if (s->func.arg2)
+ host = s->func.arg2;
+ else {
+ if (s->sel->selector == SEL_NETGRP)
+ v = macro_findvar(sv, "host", 4);
+ else
+ v = macro_findvar(sv, "hostd", 5);
+ if (!v || !*v->val) {
+ error(logopt,
+ "failed to get value of ${host}");
+ break;
+ }
+ host = v->val;
+ }
+ ret = innetgr(s->func.arg1, host, NULL, NULL);
+ if (s->compare == SEL_COMP_NOT)
+ ret = !ret;
+ if (ret) {
+ if (!s->func.arg2)
+ debug(logopt, MODPREFIX
+ "matched selector %s(%s)",
+ s->sel->name, s->func.arg1);
+ else
+ debug(logopt, MODPREFIX
+ "matched selector %s(%s,%s)",
+ s->sel->name, s->func.arg1,
+ s->func.arg2);
+ } else {
+ if (!s->func.arg2)
+ debug(logopt, MODPREFIX
+ "did not match selector %s(%s)",
+ s->sel->name, s->func.arg1);
+ else
+ debug(logopt, MODPREFIX
+ "did not match selector %s(%s,%s)",
+ s->sel->name, s->func.arg1, s->func.arg2);
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
static void update_with_defaults(struct amd_entry *defaults,
struct amd_entry *entry,
struct substvar *sv)
@@ -884,6 +1185,33 @@ static void update_prefix(struct autofs_point *ap,
return;
}
+static int match_selectors(unsigned int logopt,
+ struct amd_entry *entry, struct substvar *sv)
+{
+ struct selector *s = entry->selector;
+ int ret;
+
+ /* No selectors, always match */
+ if (!s) {
+ debug(logopt, "no selectors found in location");
+ return 1;
+ }
+
+ ret = 0;
+
+ /* All selectors must match */
+ while (s) {
+ ret = eval_selector(logopt, entry, sv);
+ if (!ret)
+ break;
+ s = s->next;
+ }
+ if (!s)
+ ret = 1;
+
+ return ret;
+}
+
static struct amd_entry *dup_defaults_entry(struct amd_entry *defaults)
{
struct amd_entry *entry;
@@ -1008,6 +1336,23 @@ static struct amd_entry *select_default_entry(struct autofs_point *ap,
free_amd_entry(this);
continue;
}
+
+ /*
+ * This probably should be a fail since we expect
+ * selectors to pick the default entry.
+ */
+ if (!this->selector)
+ continue;
+
+ if (match_selectors(ap->logopt, this, sv)) {
+ if (entry_default) {
+ /*update_with_defaults(entry_default, this, sv);*/
+ free_amd_entry(entry_default);
+ }
+ list_del_init(&this->list);
+ defaults_entry = this;
+ break;
+ }
}
/* Not strickly amd semantics but ... */
@@ -1195,6 +1540,9 @@ int parse_mount(struct autofs_point *ap, const char *name,
continue;
}
+ if (!match_selectors(ap->logopt, this, sv))
+ continue;
+
update_with_defaults(cur_defaults, this, sv);
sv = expand_entry(ap, this, flags, sv);
sv = merge_entry_options(ap, this, sv);