Helo, Omar

Thank you for the patch!

• Omar Polo [2023-12-14 20:01]:
[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.)

cvs checkout took looong time, so I took src.tar.gz from 7.4, applied patch there.

diff worked for static maps, at least. tested on this config:

<config>
table local-interfaces { ::1 127.0.0.1 }

table helo-names { \
  127.0.0.1 = localhost, \
  ::1 = localhost, \
  "[::1]" = localhost, \
  ipv6:::1 = localhost \
}

listen on socket
listen on lo0

action mail_relay relay host smtp://[::1] \
  src <local-interfaces> \
  helo-src <helo-names>

match from local for any action mail_relay
</config>

from logs:

mproc: dispatcher -> lka : 56 IMSG_MTA_LOOKUP_HELO
imsg: lka <- dispatcher: IMSG_MTA_LOOKUP_HELO (len=56)
lookup: lookup "[::1]" as ADDRNAME in table static:helo-names -> "localhost"
mproc: lka -> dispatcher : 23 IMSG_MTA_LOOKUP_HELO
imsg: dispatcher <- lka: IMSG_MTA_LOOKUP_HELO (len=23)
[...]
d147a0751842134 smtp connected address=[::1] host=localhost.localdomain
smtp: 0x1d0c9690000: >>> 220 build ESMTP OpenSMTPD
smtp: 0x1d0c9690000: IO_LOWAT <io:0x1cff3822e00 fd=13 to=300000 fl=W ib=0 ob=0> mta: 0x1d084c9d6d0: IO_DATAIN <io:0x1d084c82d00 fd=12 to=300000 fl=R ib=27 ob=0>
mta: 0x1d084c9d6d0: <<< 220 build ESMTP OpenSMTPD
mta: 0x1d084c9d6d0: MTA_BANNER -> MTA_EHLO
mta: 0x1d084c9d6d0: >>> EHLO localhost

so this is good!

but didn't work with "table helo-names file:/etc/mail/helo-names", however, which contained:

127.0.0.1 localhost1
::1 localhost2
[::1] localhost3
ipv6:::1 localhost4

with file based map logs showed:

imsg: lka <- dispatcher: IMSG_MTA_LOOKUP_HELO (len=56)
lookup: lookup "[::1]" as ADDRNAME in table static:helo-names -> none
mproc: lka -> dispatcher : 12 IMSG_MTA_LOOKUP_HELO
imsg: dispatcher <- lka: IMSG_MTA_LOOKUP_HELO (len=12)

when I did delivery to 127.0.0.1, file based map returned proper name:

mproc: dispatcher -> lka : 44 IMSG_MTA_LOOKUP_HELO
imsg: lka <- dispatcher: IMSG_MTA_LOOKUP_HELO (len=44)
lookup: lookup "127.0.0.1" as ADDRNAME in table static:helo-names -> "localhost1"
mproc: lka -> dispatcher : 24 IMSG_MTA_LOOKUP_HELO
imsg: dispatcher <- lka: IMSG_MTA_LOOKUP_HELO (len=24)

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