normalize localparts with unicode nfc when parsing

both when parsing our configs, and for incoming on smtp or in messages.
so we properly compare things like é and e+accent as equal, and accept the
different encodings of that same address.
This commit is contained in:
Mechiel Lukkien
2024-03-08 21:08:40 +01:00
parent 4fbd7abb57
commit 8e6fe7459b
23 changed files with 134 additions and 59 deletions

View File

@ -36,6 +36,7 @@ import (
_ "embed"
"golang.org/x/exp/maps"
"golang.org/x/text/unicode/norm"
"github.com/mjl-/adns"
@ -1880,7 +1881,7 @@ func (Admin) DomainAdd(ctx context.Context, domain, accountName, localpart strin
d, err := dns.ParseDomain(domain)
xcheckuserf(ctx, err, "parsing domain")
err = mox.DomainAdd(ctx, d, accountName, smtp.Localpart(localpart))
err = mox.DomainAdd(ctx, d, accountName, smtp.Localpart(norm.NFC.String(localpart)))
xcheckf(ctx, err, "adding domain")
}

View File

@ -1376,7 +1376,7 @@
},
{
"Name": "Unicode",
"Docs": "Name as U-labels. Empty if this is an ASCII-only domain. No trailing dot.",
"Docs": "Name as U-labels, in Unicode NFC. Empty if this is an ASCII-only domain. No trailing dot.",
"Typewords": [
"string"
]
@ -4639,7 +4639,7 @@
},
{
"Name": "Localpart",
"Docs": "Localpart is a decoded local part of an email address, before the \"@\".\nFor quoted strings, values do not hold the double quote or escaping backslashes.\nAn empty string can be a valid localpart.",
"Docs": "Localpart is a decoded local part of an email address, before the \"@\".\nFor quoted strings, values do not hold the double quote or escaping backslashes.\nAn empty string can be a valid localpart.\nLocalparts are in Unicode NFC.",
"Values": null
},
{

View File

@ -43,7 +43,7 @@ export interface IPRevCheckResult {
// trailing dot. When using with StrictResolver, add the trailing dot.
export interface Domain {
ASCII: string // A non-unicode domain, e.g. with A-labels (xn--...) or NR-LDH (non-reserved letters/digits/hyphens) labels. Always in lower case. No trailing dot.
Unicode: string // Name as U-labels. Empty if this is an ASCII-only domain. No trailing dot.
Unicode: string // Name as U-labels, in Unicode NFC. Empty if this is an ASCII-only domain. No trailing dot.
}
export interface MXCheckResult {
@ -765,6 +765,7 @@ export enum SPFResult {
// Localpart is a decoded local part of an email address, before the "@".
// For quoted strings, values do not hold the double quote or escaping backslashes.
// An empty string can be a valid localpart.
// Localparts are in Unicode NFC.
export type Localpart = string
// An IP is a single IP address, a slice of bytes.