unbreak parsing of IPv6 addresses in file-backed table(5)s
authorop <op@openbsd.org>
Thu, 2 May 2024 18:14:33 +0000 (18:14 +0000)
committerop <op@openbsd.org>
Thu, 2 May 2024 18:14:33 +0000 (18:14 +0000)
The file parser splits the line on the ':' character too for key-value
tables, and so mis-parses IPv6 addresses.  The "::1 localhost" example
in table(5) is actually parsed as key "" and value ":1 localhost".

For list tables, the "# @list" marker can be used as a workaround, but
for key-valued the parser has to be fixed.

There are also some weird edge cases when splitting the lines.

Now the parser always splits on the first whitespace or colon, and then
strips the spaces.  For lines starting with '[' the parser will jump to
the matching ']' before attempting to split.  So, for example:

[::1]:localhost becomes "[::1]" -> "localhost"
[::1] example.org becomes "[::1]" -> "example.org"
foo: bar becomes "foo" -> "bar"
foo::bar becomes "foo" -> ":bar"
foo : bar becomes "foo" -> ": bar"

etc...

This only affects the parser for file table(5)s and makemap(8).  Inline
tables or "proc" tables are unaffected.

ok gilles@

usr.sbin/smtpd/table.5
usr.sbin/smtpd/util.c

index 65dca35..3f4d409 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: table.5,v 1.13 2023/12/27 11:29:56 op Exp $
+.\"    $OpenBSD: table.5,v 1.14 2024/05/02 18:14:33 op Exp $
 .\"
 .\" Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
 .\" Copyright (c) 2013 Gilles Chehade <gilles@poolp.org>
@@ -16,7 +16,7 @@
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
 .\"
-.Dd $Mdocdate: December 27 2023 $
+.Dd $Mdocdate: May 2 2024 $
 .Dt TABLE 5
 .Os
 .Sh NAME
@@ -173,10 +173,11 @@ of addresses in the table until a match is found.
 A netaddr table can contain exact addresses or netmasks, and looks as follow:
 .Bd -literal -offset indent
 192.168.1.1
-::1
-ipv6:::1
+[::1]
 192.168.1.0/24
 .Ed
+.Pp
+IPv6 addresses must be enclosed in square brackets.
 .Ss Userinfo tables
 Userinfo tables are used in rule context to specify an alternate userbase,
 mapping virtual users to local system users by UID, GID and home directory.
@@ -214,11 +215,11 @@ A source table looks as follow:
 .Bd -literal -offset indent
 192.168.1.2
 192.168.1.3
-::1
-::2
-ipv6:::3
-ipv6:::4
+[::1]
+[::2]
 .Ed
+.Pp
+IPv6 address must be enclosed in square brackets.
 .Ss Mailaddr tables
 Mailaddr tables are lists of email addresses.
 They can be used in the following contexts:
@@ -254,10 +255,12 @@ outgoing connection.
 .Pp
 The format is a mapping from inet4 or inet6 addresses to hostnames:
 .Bd -literal -offset indent
-::1            localhost
+[::1]          localhost
 127.0.0.1      localhost
 88.190.23.165  www.opensmtpd.org
 .Ed
+.Pp
+IPv6 addresses must be enclosed in square brackets.
 .Sh SEE ALSO
 .Xr smtpd.conf 5 ,
 .Xr makemap 8 ,
index d481162..e2e0322 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: util.c,v 1.156 2024/02/11 09:24:26 op Exp $   */
+/*     $OpenBSD: util.c,v 1.157 2024/05/02 18:14:33 op Exp $   */
 
 /*
  * Copyright (c) 2000,2001 Markus Friedl.  All rights reserved.
@@ -855,7 +855,7 @@ int
 parse_table_line(FILE *fp, char **line, size_t *linesize,
     int *type, char **key, char **val, int *malformed)
 {
-       char    *keyp, *valp, *p;
+       char    *keyp, *valp;
        ssize_t  linelen;
 
        *key = NULL;
@@ -885,16 +885,17 @@ parse_table_line(FILE *fp, char **line, size_t *linesize,
                return 0;
        }
 
-       if (*type == T_NONE) {
-               for (p = keyp; *p; p++) {
-                       if (*p == ' ' || *p == '\t' || *p == ':') {
-                               *type = T_HASH;
-                               break;
-                       }
+       if (*keyp == '[') {
+               if ((valp = strchr(keyp, ']')) == NULL) {
+                       *malformed = 1;
+                       return (0);
                }
-               if (*type == T_NONE)
-                       *type = T_LIST;
-       }
+               valp++;
+       } else
+               valp = keyp + strcspn(keyp, " \t:");
+
+       if (*type == T_NONE)
+               *type = (*valp == '\0') ? T_LIST : T_HASH;
 
        if (*type == T_LIST) {
                *key = keyp;
@@ -902,20 +903,11 @@ parse_table_line(FILE *fp, char **line, size_t *linesize,
        }
 
        /* T_HASH */
-       valp = keyp;
-       strsep(&valp, " \t:");
-       if (valp) {
-               while (*valp) {
-                       if (!isspace((unsigned char)*valp) &&
-                           !(*valp == ':' &&
-                           isspace((unsigned char)*(valp + 1))))
-                               break;
-                       ++valp;
-               }
-               if (*valp == '\0')
-                       valp = NULL;
+       if (*valp != '\0') {
+               *valp++ = '\0';
+               valp += strspn(valp, " \t");
        }
-       if (valp == NULL)
+       if (*valp == '\0')
                *malformed = 1;
 
        *key = keyp;