@@ -32,6 +32,7 @@ import (
32
32
"time"
33
33
34
34
"golang.org/x/crypto/acme"
35
+ "golang.org/x/net/idna"
35
36
)
36
37
37
38
// createCertRetryAfter is how much time to wait before removing a failed state
@@ -63,12 +64,15 @@ type HostPolicy func(ctx context.Context, host string) error
63
64
// Only exact matches are currently supported. Subdomains, regexp or wildcard
64
65
// will not match.
65
66
//
66
- // Note that all hosts will be converted to lowercase via strings.ToLower so that
67
- // Manager.GetCertificate can handle mixedcase hosts correctly.
67
+ // Note that all hosts will be converted to Punycode via idna.Lookup.ToASCII so that
68
+ // Manager.GetCertificate can handle the Unicode IDN and mixedcase hosts correctly.
69
+ // Invlaid hosts will be silently ignored.
68
70
func HostWhitelist (hosts ... string ) HostPolicy {
69
71
whitelist := make (map [string ]bool , len (hosts ))
70
72
for _ , h := range hosts {
71
- whitelist [strings .ToLower (h )] = true
73
+ if h , err := idna .Lookup .ToASCII (h ); err == nil {
74
+ whitelist [h ] = true
75
+ }
72
76
}
73
77
return func (_ context.Context , host string ) error {
74
78
if ! whitelist [host ] {
@@ -239,14 +243,25 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
239
243
return nil , errors .New ("acme/autocert: Manager.Prompt not set" )
240
244
}
241
245
242
- name := strings . ToLower ( hello .ServerName )
246
+ name := hello .ServerName
243
247
if name == "" {
244
248
return nil , errors .New ("acme/autocert: missing server name" )
245
249
}
246
250
if ! strings .Contains (strings .Trim (name , "." ), "." ) {
247
251
return nil , errors .New ("acme/autocert: server name component count invalid" )
248
252
}
249
- if strings .ContainsAny (name , `+/\` ) {
253
+
254
+ // Note that this conversion is necessary because some server names in the handshakes
255
+ // made by some clients (like cURL) is not implicitly converted to Punycode, which will
256
+ // cause the certificate to fail to be obtained. In addition, we should also treat
257
+ // example.com and EXAMPLE.COM as equivalent and must return the same certificate for
258
+ // them. Fortunately, this conversion also helped us deal with this kind of mixedcase
259
+ // problems.
260
+ //
261
+ // Due to the "σςΣ" problem (see https://unicode.org/faq/idn.html#22), we can't use
262
+ // idna.Punycode.ToASCII (or just idna.ToASCII) here.
263
+ name , err := idna .Lookup .ToASCII (hello .ServerName )
264
+ if err != nil {
250
265
return nil , errors .New ("acme/autocert: server name contains invalid character" )
251
266
}
252
267
0 commit comments