[moving to tech@]

On 2023/12/13 20:37:09 +0100, Kirill Miazine <k...@krot.org> wrote:
> I've spent several hours debugging an issue.
> 
> table(5) specifies addrname format as a mapping from inet4 or inet6 
> addresses to hostnames:
> 
>             ::1             localhost
>             127.0.0.1       localhost
>             88.190.23.165   www.opensmtpd.org
> 
> But I can't get IPv6 mappings to work. So I've given up, and use helo 
> instead of helo-src.
> 
> But helo-src is useful (not on this particular setup, though).
> 
> For a month or so I had a static addrname table like this:
> 
> table helo-names { \
>    65.108.153.125 = com.krot.org, \
>    2a01:4f9:c010:9411::1 = com.krot.org
> }
> 
> I suppose this was the cause of the frequent "Failed to retrieve helo 
> string" errors I was getting -- smtpd would not get helo name for IPv6 
> address.
> 
> At first I thought it was space around equal signs, but table(5) uses 
> space in the example. So I had to dig further.
> 
> Doing tracing, I saw that for IPv6 it -- apparently -- would be looking 
> up IPv6 address enclosed in brackets:
> 
> lookup: lookup "[2a01:4f9:c010:9411::1]" as ADDRNAME in table 
> static:helo-names -> none
> 
> So, I went ahead and did:
> 
> table helo-names { \
>    65.108.153.125 = com.krot.org, \
>    "[2a01:4f9:c010:9411::1]" = com.krot.org \
> }

(by the way, in -current the \ are no longer needed.)

> This didn't help, either:
> 
> lookup: lookup "[2a01:4f9:c010:9411::1]" as ADDRNAME in table 
> static:helo-names -> "com.krot.org"
> warn: failure during helo lookup helo-names:[2a01:4f9:c010:9411::1]
> 
> Now it did find "com.krot.org", but why did it report "failure during 
> helo lookup"? It found a match, but still reported failure...

The issue is that smtpd fails to parse the address.

First, the sockaddr is turned into a string using sa_to_text() that
wraps ipv6 addresses in brackets.  The resulting string is then searched
in the table and (if found) passed to inet_pton to get back a sockaddr,
but the [] are left in there, and so it fails.

I don't think it's safe to change sa_to_text() to not wrap ipv6
addresses in brackets, but we can adjust parse_sockaddr().

See diff below.

> Thinking that it could be a static-map issue, I did a file: map. First 
> as documented in the man page:
> 
> 2a01:4f9:c010:9411::1           com.krot.org
> 65.108.153.125                  com.krot.org
> 
> This didn't work:
> 
> lookup: lookup "[2a01:4f9:c010:9411::1]" as ADDRNAME in table 
> static:helo-names -> none
> 
> Then with square brackets:
> 
> 65.108.153.125                  com.krot.org
> [2a01:4f9:c010:9411::1]         com.krot.org
> 
> And this time square bracked didn't help.
> 
> lookup: lookup "[2a01:4f9:c010:9411::1]" as ADDRNAME in table 
> static:helo-names -> none
> 
> New try with db:
> 
> makemap -d hash -o helo-names.db -t set helo-names
> 
> When reading makemap(8), I noticed this:
> 
> In all cases, makemap reads lines consisting of words separated by 
> whitespace. The first word of a line is the database key; the remainder 
> represents the mapped value.  The database key and value may optionally 
> be separated by the colon character.
> 
> Colon character! In makemap.c there's:
> 
>          strsep(&valp, " \t:");
> 
> So it would be splitting IPv6 addresses, IIUC.
> 
> table(5) doesn't say anything about colons.
> 
> So I did a dump, and it indeed does separate the IPv6 address:
> 
> # makemap -U helo-names.db 
> 
> 65.108.153.125  com.krot.org
> [2a01   4f9:c010:9411::1]               com.krot.org
> 
> How would one put IPv6 addresses in such a map?

I don't think makemap as-is supports ipv6 addresses, but this should at
least fix the usage in other tables.

Can you please test it and report back if it works?  I have to admit I
failed to test helo-src locally so I hacked up something in smtpd.c to
perform a lookup in order to test this.

(I *believe* the diff should apply to -portable as well as-is.)

diff /tmp/smtpd
commit - 59829af3c4da38e511c4f8e3e4a38e45fcf3b082
path + /tmp/smtpd
blob - 7328cf5df6eb0034e9bb5c91b59c74bd3d402db4
file + table.c
--- table.c
+++ table.c
@@ -628,7 +628,8 @@ parse_sockaddr(struct sockaddr *sa, int family, const 
        struct in6_addr          in6a;
        struct sockaddr_in      *sin;
        struct sockaddr_in6     *sin6;
-       char                    *cp, *str2;
+       char                    *cp;
+       char                     addr[NI_MAXHOST];
        const char              *errstr;
 
        switch (family) {
@@ -649,23 +650,21 @@ parse_sockaddr(struct sockaddr *sa, int family, const 
                return (0);
 
        case PF_INET6:
-               if (strncasecmp("ipv6:", str, 5) == 0)
+               if (*str == '[')
+                       str++;
+               if (!strncasecmp("ipv6:", str, 5))
                        str += 5;
-               cp = strchr(str, SCOPE_DELIMITER);
-               if (cp) {
-                       str2 = strdup(str);
-                       if (str2 == NULL)
-                               return (-1);
-                       str2[cp - str] = '\0';
-                       if (inet_pton(PF_INET6, str2, &in6a) != 1) {
-                               free(str2);
-                               return (-1);
-                       }
-                       cp++;
-                       free(str2);
-               } else if (inet_pton(PF_INET6, str, &in6a) != 1)
+
+               if (strlcpy(addr, str, sizeof(addr)) >= sizeof(addr))
                        return (-1);
+               if ((cp = strchr(addr, ']')) != NULL)
+                       *cp = '\0';
+               if ((cp = strchr(addr, SCOPE_DELIMITER)) != NULL)
+                       *cp++ = '\0';
 
+               if (inet_pton(PF_INET6, addr, &in6a) != 1)
+                       return (-1);
+
                sin6 = (struct sockaddr_in6 *)sa;
                memset(sin6, 0, sizeof *sin6);
                sin6->sin6_len = sizeof(struct sockaddr_in6);





Reply via email to