Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
Re: smtpd: allow braces for `listen' options
To:
Omar Polo <op@omarpolo.com>
Cc:
tech@openbsd.org
Date:
Mon, 21 Oct 2024 20:17:41 +0200

Download raw body.

Thread
On Mon, 21 Oct 2024 19:03:22 +0200,
Omar Polo <op@omarpolo.com> wrote:
>
> unfortunately not.  shift-reduce (or reduce-reduce) conflicts are due to
> an ambiguous grammar.  I'm trying to keep smtpd' grammar non ambiguous,
> since that has also other interesting properties :-)
> 
> The issue here is at a lower level.  The lexer will, say, emit a token
> LISTEN whenever it sees "listen" in the input file, regardless of where
> we are in the config.  Then, we require { ... } to have only STRING
> inside, but the parser doesn't know it, and yacc reports a syntax error
> since it has found a, say, LISTEN token instead of a STRING one.
> 
> (and afaik we don't have a way to tell yacc to accept any token in a
> grammar rule)
> 
> I was wandering about adding a flag to control the lexer, but it's too
> fragile and also a bit ugly.  Luckily, I think this in practice will not
> occur often, and quoting of reserved keywords is already required.  So,
> I believe that your idea of parsing "-foo" as a string instead of
> erroring could actually work out.  I still have to study the
> implications of doing so however.  (ENOTENOUGHTIME unfortunately :-/)

ah, when here a bit ugly way which allows me to parse a block like this:

filter dnsbl proc-exec {
	/tmp/filter-dnsbl -m
    action ca 123 -123
		domain3 domain4
}

and execute /tmp/filter-dnsbl with args: -m action ca 123 -123 domain3 domain4

here an updated diff:

Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/smtpd/parse.y,v
diff -u -p -r1.299 parse.y
--- parse.y	19 Feb 2024 21:00:19 -0000	1.299
+++ parse.y	21 Oct 2024 18:16:15 -0000
@@ -162,6 +162,8 @@ typedef struct {
 	int lineno;
 } YYSTYPE;
 
+static int lookup_as_string;
+
 %}
 
 %token	ACTION ADMD ALIAS ANY ARROW AUTH AUTH_OPTIONAL
@@ -191,6 +193,7 @@ typedef struct {
 %token	<v.string>	STRING
 %token  <v.number>	NUMBER
 %type	<v.table>	table
+%type	<v.string>	numberstr cmdline cmdline_l
 %type	<v.number>	size negation
 %type	<v.table>	tables tablenew tableref
 %%
@@ -295,6 +298,34 @@ tableval_list	: string_list			{ }
 		| keyval_list			{ }
 		;
 
+numberstr:
+STRING
+| NUMBER {
+	if (asprintf(&$$, "%lld", (long long)$1) == -1)
+		fatalx("asprintf");
+}
+;
+
+cmdline_l:
+numberstr optnl { $$ = $1; }
+| cmdline_l numberstr optnl {
+	if (asprintf(&$$, "%s %s", $1, $2) == -1)
+		fatalx("asprint");
+	free($1);
+	free($2);
+}
+;
+
+cmdline:
+STRING
+| '{' optnl {
+    lookup_as_string = 1;
+} cmdline_l '}' {
+    lookup_as_string = 0;
+    $$ = $4;
+  }
+;
+
 bounce:
 BOUNCE WARN_INTERVAL {
 	memset(conf->sc_bounce_warn, 0, sizeof conf->sc_bounce_warn);
@@ -1911,7 +1942,7 @@ FILTER STRING PROC STRING {
 	filter_config = NULL;
 }
 |
-FILTER STRING PROC_EXEC STRING {
+FILTER STRING PROC_EXEC cmdline {
 	if (dict_get(conf->sc_filters_dict, $2)) {
 		yyerror("filter already exists with that name: %s", $2);
 		free($2);
@@ -2752,7 +2783,7 @@ lookup(char *s)
 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
 	    sizeof(keywords[0]), kw_cmp);
 
-	if (p)
+	if (p && !lookup_as_string)
 		return (p->k_val);
 	else
 		return (STRING);
@@ -2889,7 +2920,7 @@ top:
 				yyerror("string too long");
 				return (findeol());
 			}
-			if (isalnum(c) || c == '_') {
+			if (isalnum(c) || c == '/' || c == '-' || c == '_') {
 				*p++ = c;
 				continue;
 			}
@@ -2983,8 +3014,6 @@ nodigits:
 			while (p > buf + 1)
 				lungetc((unsigned char)*--p);
 			c = (unsigned char)*--p;
-			if (c == '-')
-				return (c);
 		}
 	}
 
@@ -3001,7 +3030,7 @@ nodigits:
 	x != '!' && x != '=' && x != '#' && \
 	x != ','))
 
-	if (isalnum(c) || c == ':' || c == '_') {
+	if (isalnum(c) || c == ':' || c == '/' || c == '-' || c == '_') {
 		do {
 			*p++ = c;
 			if ((size_t)(p-buf) >= sizeof(buf)) {


-- 
wbr, Kirill