1/*2Maddy Mail Server - Composable all-in-one email server.3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors45This program is free software: you can redistribute it and/or modify6it under the terms of the GNU General Public License as published by7the Free Software Foundation, either version 3 of the License, or8(at your option) any later version.910This program is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with this program. If not, see <https://www.gnu.org/licenses/>.17*/1819package dns2021import (22 "strings"2324 "github.com/miekg/dns"25 "golang.org/x/net/idna"26 "golang.org/x/text/unicode/norm"27)2829func FQDN(domain string) string {30 return dns.Fqdn(domain)31}3233// ForLookup converts the domain into a canonical form suitable for table34// lookups and other comparisons.35//36// TL;DR Use this instead of strings.ToLower to prepare domain for lookups.37//38// Domains that contain invalid UTF-8 or invalid A-label39// domains are simply converted to local-case using strings.ToLower, but the40// error is also returned.41func ForLookup(domain string) (string, error) {42 uDomain, err := idna.ToUnicode(domain)43 if err != nil {44 return strings.ToLower(domain), err45 }4647 // Side note: strings.ToLower does not support full case-folding, so it is48 // important to apply NFC normalization first.49 uDomain = norm.NFC.String(uDomain)50 uDomain = strings.ToLower(uDomain)51 uDomain = strings.TrimSuffix(uDomain, ".")52 return uDomain, nil53}5455// Equal reports whether domain1 and domain2 are equivalent as defined by56// IDNA2008 (RFC 5890).57//58// TL;DR Use this instead of strings.EqualFold to compare domains.59//60// Equivalence for malformed A-label domains is defined using regular61// byte-string comparison with case-folding applied.62func Equal(domain1, domain2 string) bool {63 // Short circult. If they are bit-equivalent, then they are also semantically64 // equivalent.65 if domain1 == domain2 {66 return true67 }6869 uDomain1, _ := ForLookup(domain1)70 uDomain2, _ := ForLookup(domain2)71 return uDomain1 == uDomain272}