[moving to tech@]
On 2023/12/13 20:37:09 +0100, Kirill Miazine <[email protected]> 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);