maddy

Fork https://github.com/foxcpp/maddy

git clone git://git.lin.moe/go/maddy.git

 1/*
 2Maddy Mail Server - Composable all-in-one email server.
 3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors
 4
 5This program is free software: you can redistribute it and/or modify
 6it under the terms of the GNU General Public License as published by
 7the Free Software Foundation, either version 3 of the License, or
 8(at your option) any later version.
 9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program.  If not, see <https://www.gnu.org/licenses/>.
17*/
18
19package dns
20
21import (
22	"strings"
23
24	"github.com/miekg/dns"
25	"golang.org/x/net/idna"
26	"golang.org/x/text/unicode/norm"
27)
28
29func FQDN(domain string) string {
30	return dns.Fqdn(domain)
31}
32
33// ForLookup converts the domain into a canonical form suitable for table
34// 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-label
39// domains are simply converted to local-case using strings.ToLower, but the
40// 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), err
45	}
46
47	// Side note: strings.ToLower does not support full case-folding, so it is
48	// 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, nil
53}
54
55// Equal reports whether domain1 and domain2 are equivalent as defined by
56// 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 regular
61// 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 semantically
64	// equivalent.
65	if domain1 == domain2 {
66		return true
67	}
68
69	uDomain1, _ := ForLookup(domain1)
70	uDomain2, _ := ForLookup(domain2)
71	return uDomain1 == uDomain2
72}